/[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.113 by wakaba, Sun Mar 16 07:07:59 2008 UTC revision 1.191 by wakaba, Mon Sep 22 06:04:29 2008 UTC
# Line 3  use strict; Line 3  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);  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  ## TODO: Control charcters and noncharacters are not allowed (HTML5 revision 1263)  require IO::Handle;
23  ## TODO: 1252 parse error (revision 1264)  
24  ## TODO: 8859-11 = 874 (revision 1271)  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
25    my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
26  my $permitted_slash_tag_name = {  my $SVG_NS = q<http://www.w3.org/2000/svg>;
27    base => 1,  my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
28    link => 1,  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
29    meta => 1,  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
30    hr => 1,  
31    br => 1,  sub A_EL () { 0b1 }
32    img => 1,  sub ADDRESS_EL () { 0b10 }
33    embed => 1,  sub BODY_EL () { 0b100 }
34    param => 1,  sub BUTTON_EL () { 0b1000 }
35    area => 1,  sub CAPTION_EL () { 0b10000 }
36    col => 1,  sub DD_EL () { 0b100000 }
37    input => 1,  sub DIV_EL () { 0b1000000 }
38    sub DT_EL () { 0b10000000 }
39    sub FORM_EL () { 0b100000000 }
40    sub FORMATTING_EL () { 0b1000000000 }
41    sub FRAMESET_EL () { 0b10000000000 }
42    sub HEADING_EL () { 0b100000000000 }
43    sub HTML_EL () { 0b1000000000000 }
44    sub LI_EL () { 0b10000000000000 }
45    sub NOBR_EL () { 0b100000000000000 }
46    sub OPTION_EL () { 0b1000000000000000 }
47    sub OPTGROUP_EL () { 0b10000000000000000 }
48    sub P_EL () { 0b100000000000000000 }
49    sub SELECT_EL () { 0b1000000000000000000 }
50    sub TABLE_EL () { 0b10000000000000000000 }
51    sub TABLE_CELL_EL () { 0b100000000000000000000 }
52    sub TABLE_ROW_EL () { 0b1000000000000000000000 }
53    sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }
54    sub MISC_SCOPING_EL () { 0b100000000000000000000000 }
55    sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }
56    sub FOREIGN_EL () { 0b10000000000000000000000000 }
57    sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }
58    sub MML_AXML_EL () { 0b1000000000000000000000000000 }
59    sub RUBY_EL () { 0b10000000000000000000000000000 }
60    sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }
61    
62    sub TABLE_ROWS_EL () {
63      TABLE_EL |
64      TABLE_ROW_EL |
65      TABLE_ROW_GROUP_EL
66    }
67    
68    ## NOTE: Used in "generate implied end tags" algorithm.
69    ## NOTE: There is a code where a modified version of END_TAG_OPTIONAL_EL
70    ## is used in "generate implied end tags" implementation (search for the
71    ## function mae).
72    sub END_TAG_OPTIONAL_EL () {
73      DD_EL |
74      DT_EL |
75      LI_EL |
76      P_EL |
77      RUBY_COMPONENT_EL
78    }
79    
80    ## NOTE: Used in </body> and EOF algorithms.
81    sub ALL_END_TAG_OPTIONAL_EL () {
82      DD_EL |
83      DT_EL |
84      LI_EL |
85      P_EL |
86    
87      BODY_EL |
88      HTML_EL |
89      TABLE_CELL_EL |
90      TABLE_ROW_EL |
91      TABLE_ROW_GROUP_EL
92    }
93    
94    sub SCOPING_EL () {
95      BUTTON_EL |
96      CAPTION_EL |
97      HTML_EL |
98      TABLE_EL |
99      TABLE_CELL_EL |
100      MISC_SCOPING_EL
101    }
102    
103    sub TABLE_SCOPING_EL () {
104      HTML_EL |
105      TABLE_EL
106    }
107    
108    sub TABLE_ROWS_SCOPING_EL () {
109      HTML_EL |
110      TABLE_ROW_GROUP_EL
111    }
112    
113    sub TABLE_ROW_SCOPING_EL () {
114      HTML_EL |
115      TABLE_ROW_EL
116    }
117    
118    sub SPECIAL_EL () {
119      ADDRESS_EL |
120      BODY_EL |
121      DIV_EL |
122    
123      DD_EL |
124      DT_EL |
125      LI_EL |
126      P_EL |
127    
128      FORM_EL |
129      FRAMESET_EL |
130      HEADING_EL |
131      OPTION_EL |
132      OPTGROUP_EL |
133      SELECT_EL |
134      TABLE_ROW_EL |
135      TABLE_ROW_GROUP_EL |
136      MISC_SPECIAL_EL
137    }
138    
139    my $el_category = {
140      a => A_EL | FORMATTING_EL,
141      address => ADDRESS_EL,
142      applet => MISC_SCOPING_EL,
143      area => MISC_SPECIAL_EL,
144      b => FORMATTING_EL,
145      base => MISC_SPECIAL_EL,
146      basefont => MISC_SPECIAL_EL,
147      bgsound => MISC_SPECIAL_EL,
148      big => FORMATTING_EL,
149      blockquote => MISC_SPECIAL_EL,
150      body => BODY_EL,
151      br => MISC_SPECIAL_EL,
152      button => BUTTON_EL,
153      caption => CAPTION_EL,
154      center => MISC_SPECIAL_EL,
155      col => MISC_SPECIAL_EL,
156      colgroup => MISC_SPECIAL_EL,
157      dd => DD_EL,
158      dir => MISC_SPECIAL_EL,
159      div => DIV_EL,
160      dl => MISC_SPECIAL_EL,
161      dt => DT_EL,
162      em => FORMATTING_EL,
163      embed => MISC_SPECIAL_EL,
164      fieldset => MISC_SPECIAL_EL,
165      font => FORMATTING_EL,
166      form => FORM_EL,
167      frame => MISC_SPECIAL_EL,
168      frameset => FRAMESET_EL,
169      h1 => HEADING_EL,
170      h2 => HEADING_EL,
171      h3 => HEADING_EL,
172      h4 => HEADING_EL,
173      h5 => HEADING_EL,
174      h6 => HEADING_EL,
175      head => MISC_SPECIAL_EL,
176      hr => MISC_SPECIAL_EL,
177      html => HTML_EL,
178      i => FORMATTING_EL,
179      iframe => MISC_SPECIAL_EL,
180      img => MISC_SPECIAL_EL,
181      input => MISC_SPECIAL_EL,
182      isindex => MISC_SPECIAL_EL,
183      li => LI_EL,
184      link => MISC_SPECIAL_EL,
185      listing => MISC_SPECIAL_EL,
186      marquee => MISC_SCOPING_EL,
187      menu => MISC_SPECIAL_EL,
188      meta => MISC_SPECIAL_EL,
189      nobr => NOBR_EL | FORMATTING_EL,
190      noembed => MISC_SPECIAL_EL,
191      noframes => MISC_SPECIAL_EL,
192      noscript => MISC_SPECIAL_EL,
193      object => MISC_SCOPING_EL,
194      ol => MISC_SPECIAL_EL,
195      optgroup => OPTGROUP_EL,
196      option => OPTION_EL,
197      p => P_EL,
198      param => MISC_SPECIAL_EL,
199      plaintext => MISC_SPECIAL_EL,
200      pre => MISC_SPECIAL_EL,
201      rp => RUBY_COMPONENT_EL,
202      rt => RUBY_COMPONENT_EL,
203      ruby => RUBY_EL,
204      s => FORMATTING_EL,
205      script => MISC_SPECIAL_EL,
206      select => SELECT_EL,
207      small => FORMATTING_EL,
208      spacer => MISC_SPECIAL_EL,
209      strike => FORMATTING_EL,
210      strong => FORMATTING_EL,
211      style => MISC_SPECIAL_EL,
212      table => TABLE_EL,
213      tbody => TABLE_ROW_GROUP_EL,
214      td => TABLE_CELL_EL,
215      textarea => MISC_SPECIAL_EL,
216      tfoot => TABLE_ROW_GROUP_EL,
217      th => TABLE_CELL_EL,
218      thead => TABLE_ROW_GROUP_EL,
219      title => MISC_SPECIAL_EL,
220      tr => TABLE_ROW_EL,
221      tt => FORMATTING_EL,
222      u => FORMATTING_EL,
223      ul => MISC_SPECIAL_EL,
224      wbr => MISC_SPECIAL_EL,
225    };
226    
227    my $el_category_f = {
228      $MML_NS => {
229        'annotation-xml' => MML_AXML_EL,
230        mi => FOREIGN_FLOW_CONTENT_EL,
231        mo => FOREIGN_FLOW_CONTENT_EL,
232        mn => FOREIGN_FLOW_CONTENT_EL,
233        ms => FOREIGN_FLOW_CONTENT_EL,
234        mtext => FOREIGN_FLOW_CONTENT_EL,
235      },
236      $SVG_NS => {
237        foreignObject => FOREIGN_FLOW_CONTENT_EL,
238        desc => FOREIGN_FLOW_CONTENT_EL,
239        title => FOREIGN_FLOW_CONTENT_EL,
240      },
241      ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
242    };
243    
244    my $svg_attr_name = {
245      attributename => 'attributeName',
246      attributetype => 'attributeType',
247      basefrequency => 'baseFrequency',
248      baseprofile => 'baseProfile',
249      calcmode => 'calcMode',
250      clippathunits => 'clipPathUnits',
251      contentscripttype => 'contentScriptType',
252      contentstyletype => 'contentStyleType',
253      diffuseconstant => 'diffuseConstant',
254      edgemode => 'edgeMode',
255      externalresourcesrequired => 'externalResourcesRequired',
256      filterres => 'filterRes',
257      filterunits => 'filterUnits',
258      glyphref => 'glyphRef',
259      gradienttransform => 'gradientTransform',
260      gradientunits => 'gradientUnits',
261      kernelmatrix => 'kernelMatrix',
262      kernelunitlength => 'kernelUnitLength',
263      keypoints => 'keyPoints',
264      keysplines => 'keySplines',
265      keytimes => 'keyTimes',
266      lengthadjust => 'lengthAdjust',
267      limitingconeangle => 'limitingConeAngle',
268      markerheight => 'markerHeight',
269      markerunits => 'markerUnits',
270      markerwidth => 'markerWidth',
271      maskcontentunits => 'maskContentUnits',
272      maskunits => 'maskUnits',
273      numoctaves => 'numOctaves',
274      pathlength => 'pathLength',
275      patterncontentunits => 'patternContentUnits',
276      patterntransform => 'patternTransform',
277      patternunits => 'patternUnits',
278      pointsatx => 'pointsAtX',
279      pointsaty => 'pointsAtY',
280      pointsatz => 'pointsAtZ',
281      preservealpha => 'preserveAlpha',
282      preserveaspectratio => 'preserveAspectRatio',
283      primitiveunits => 'primitiveUnits',
284      refx => 'refX',
285      refy => 'refY',
286      repeatcount => 'repeatCount',
287      repeatdur => 'repeatDur',
288      requiredextensions => 'requiredExtensions',
289      requiredfeatures => 'requiredFeatures',
290      specularconstant => 'specularConstant',
291      specularexponent => 'specularExponent',
292      spreadmethod => 'spreadMethod',
293      startoffset => 'startOffset',
294      stddeviation => 'stdDeviation',
295      stitchtiles => 'stitchTiles',
296      surfacescale => 'surfaceScale',
297      systemlanguage => 'systemLanguage',
298      tablevalues => 'tableValues',
299      targetx => 'targetX',
300      targety => 'targetY',
301      textlength => 'textLength',
302      viewbox => 'viewBox',
303      viewtarget => 'viewTarget',
304      xchannelselector => 'xChannelSelector',
305      ychannelselector => 'yChannelSelector',
306      zoomandpan => 'zoomAndPan',
307    };
308    
309    my $foreign_attr_xname = {
310      'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
311      'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
312      'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
313      'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
314      'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
315      'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
316      'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
317      'xml:base' => [$XML_NS, ['xml', 'base']],
318      'xml:lang' => [$XML_NS, ['xml', 'lang']],
319      'xml:space' => [$XML_NS, ['xml', 'space']],
320      'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
321      'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
322  };  };
323    
324  my $c1_entity_char = {  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
325    
326    my $charref_map = {
327      0x0D => 0x000A,
328    0x80 => 0x20AC,    0x80 => 0x20AC,
329    0x81 => 0xFFFD,    0x81 => 0xFFFD,
330    0x82 => 0x201A,    0x82 => 0x201A,
# Line 59  my $c1_entity_char = { Line 357  my $c1_entity_char = {
357    0x9D => 0xFFFD,    0x9D => 0xFFFD,
358    0x9E => 0x017E,    0x9E => 0x017E,
359    0x9F => 0x0178,    0x9F => 0x0178,
360  }; # $c1_entity_char  }; # $charref_map
361    $charref_map->{$_} = 0xFFFD
362  my $special_category = {      for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
363    address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,          0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
364    blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1,          0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
365    dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1,          0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
366    form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1,          0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
367    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1,          0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
368    img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1,          0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
   menu => 1, meta => 1, noembed => 1, noframes => 1, noscript => 1,  
   ol => 1, optgroup => 1, option => 1, p => 1, param => 1, plaintext => 1,  
   pre => 1, script => 1, select => 1, spacer => 1, style => 1, tbody => 1,  
   textarea => 1, tfoot => 1, thead => 1, title => 1, tr => 1, ul => 1, wbr => 1,  
 };  
 my $scoping_category = {  
   applet => 1, button => 1, caption => 1, html => 1, marquee => 1, object => 1,  
   table => 1, td => 1, th => 1,  
 };  
 my $formatting_category = {  
   a => 1, b => 1, big => 1, em => 1, font => 1, i => 1, nobr => 1,  
   s => 1, small => 1, strile => 1, strong => 1, tt => 1, u => 1,  
 };  
 # $phrasing_category: all other elements  
369    
370  sub parse_byte_string ($$$$;$) {  sub parse_byte_string ($$$$;$) {
371      my $self = shift;
372      my $charset_name = shift;
373      open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
374      return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
375    } # parse_byte_string
376    
377    sub parse_byte_stream ($$$$;$$) {
378      # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
379    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
380    my $charset = shift;    my $charset_name = shift;
381    my $bytes_s = ref $_[0] ? $_[0] : \($_[0]);    my $byte_stream = $_[0];
   my $s;  
     
   if (defined $charset) {  
     require Encode; ## TODO: decode(utf8) don't delete BOM  
     $s = \ (Encode::decode ($charset, $$bytes_s));  
     $self->{input_encoding} = lc $charset; ## TODO: normalize name  
     $self->{confident} = 1;  
   } else {  
     ## TODO: Implement HTML5 detection algorithm  
     require Whatpm::Charset::UniversalCharDet;  
     $charset = Whatpm::Charset::UniversalCharDet->detect_byte_string  
         (substr ($$bytes_s, 0, 1024));  
     $charset ||= 'windows-1252';  
     $s = \ (Encode::decode ($charset, $$bytes_s));  
     $self->{input_encoding} = $charset;  
     $self->{confident} = 0;  
   }  
382    
383    $self->{change_encoding} = sub {    my $onerror = $_[2] || sub {
384      my $self = shift;      my (%opt) = @_;
385      my $charset = lc shift;      warn "Parse error ($opt{type})\n";
386      ## TODO: if $charset is supported    };
387      ## TODO: normalize charset name    $self->{parse_error} = $onerror; # updated later by parse_char_string
388    
389      ## "Change the encoding" algorithm:    my $get_wrapper = $_[3] || sub ($) {
390        return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
391      ## Step 1        };
392      if ($charset eq 'utf-16') { ## ISSUE: UTF-16BE -> UTF-8? UTF-16LE -> UTF-8?  
393        $charset = 'utf-8';    ## HTML5 encoding sniffing algorithm
394      require Message::Charset::Info;
395      my $charset;
396      my $buffer;
397      my ($char_stream, $e_status);
398    
399      SNIFFING: {
400        ## NOTE: By setting |allow_fallback| option true when the
401        ## |get_decode_handle| method is invoked, we ignore what the HTML5
402        ## spec requires, i.e. unsupported encoding should be ignored.
403          ## TODO: We should not do this unless the parser is invoked
404          ## in the conformance checking mode, in which this behavior
405          ## would be useful.
406    
407        ## Step 1
408        if (defined $charset_name) {
409          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
410              ## TODO: Is this ok?  Transfer protocol's parameter should be
411              ## interpreted in its semantics?
412    
413          ($char_stream, $e_status) = $charset->get_decode_handle
414              ($byte_stream, allow_error_reporting => 1,
415               allow_fallback => 1);
416          if ($char_stream) {
417            $self->{confident} = 1;
418            last SNIFFING;
419          } else {
420            !!!parse-error (type => 'charset:not supported',
421                            layer => 'encode',
422                            line => 1, column => 1,
423                            value => $charset_name,
424                            level => $self->{level}->{uncertain});
425          }
426      }      }
427    
428      ## Step 2      ## Step 2
429      if (defined $self->{input_encoding} and      my $byte_buffer = '';
430          $self->{input_encoding} eq $charset) {      for (1..1024) {
431          my $char = $byte_stream->getc;
432          last unless defined $char;
433          $byte_buffer .= $char;
434        } ## TODO: timeout
435    
436        ## Step 3
437        if ($byte_buffer =~ /^\xFE\xFF/) {
438          $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
439          ($char_stream, $e_status) = $charset->get_decode_handle
440              ($byte_stream, allow_error_reporting => 1,
441               allow_fallback => 1, byte_buffer => \$byte_buffer);
442        $self->{confident} = 1;        $self->{confident} = 1;
443        return;        last SNIFFING;
444        } elsif ($byte_buffer =~ /^\xFF\xFE/) {
445          $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
446          ($char_stream, $e_status) = $charset->get_decode_handle
447              ($byte_stream, allow_error_reporting => 1,
448               allow_fallback => 1, byte_buffer => \$byte_buffer);
449          $self->{confident} = 1;
450          last SNIFFING;
451        } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
452          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
453          ($char_stream, $e_status) = $charset->get_decode_handle
454              ($byte_stream, allow_error_reporting => 1,
455               allow_fallback => 1, byte_buffer => \$byte_buffer);
456          $self->{confident} = 1;
457          last SNIFFING;
458      }      }
459    
460      !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}.      ## Step 4
461          ':'.$charset, level => 'w');      ## TODO: <meta charset>
462    
463      ## Step 3      ## Step 5
464      # if (can) {      ## TODO: from history
       ## change the encoding on the fly.  
       #$self->{confident} = 1;  
       #return;  
     # }  
465    
466      ## Step 4      ## Step 6
467      throw Whatpm::HTML::RestartParser (charset => $charset);      require Whatpm::Charset::UniversalCharDet;
468        $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
469            ($byte_buffer);
470        if (defined $charset_name) {
471          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
472    
473          ## ISSUE: Unsupported encoding is not ignored according to the spec.
474          require Whatpm::Charset::DecodeHandle;
475          $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
476              ($byte_stream);
477          ($char_stream, $e_status) = $charset->get_decode_handle
478              ($buffer, allow_error_reporting => 1,
479               allow_fallback => 1, byte_buffer => \$byte_buffer);
480          if ($char_stream) {
481            $buffer->{buffer} = $byte_buffer;
482            !!!parse-error (type => 'sniffing:chardet',
483                            text => $charset_name,
484                            level => $self->{level}->{info},
485                            layer => 'encode',
486                            line => 1, column => 1);
487            $self->{confident} = 0;
488            last SNIFFING;
489          }
490        }
491    
492        ## Step 7: default
493        ## TODO: Make this configurable.
494        $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
495            ## NOTE: We choose |windows-1252| here, since |utf-8| should be
496            ## detectable in the step 6.
497        require Whatpm::Charset::DecodeHandle;
498        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
499            ($byte_stream);
500        ($char_stream, $e_status)
501            = $charset->get_decode_handle ($buffer,
502                                           allow_error_reporting => 1,
503                                           allow_fallback => 1,
504                                           byte_buffer => \$byte_buffer);
505        $buffer->{buffer} = $byte_buffer;
506        !!!parse-error (type => 'sniffing:default',
507                        text => 'windows-1252',
508                        level => $self->{level}->{info},
509                        line => 1, column => 1,
510                        layer => 'encode');
511        $self->{confident} = 0;
512      } # SNIFFING
513    
514      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
515        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
516        !!!parse-error (type => 'chardecode:fallback',
517                        #text => $self->{input_encoding},
518                        level => $self->{level}->{uncertain},
519                        line => 1, column => 1,
520                        layer => 'encode');
521      } elsif (not ($e_status &
522                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
523        $self->{input_encoding} = $charset->get_iana_name;
524        !!!parse-error (type => 'chardecode:no error',
525                        text => $self->{input_encoding},
526                        level => $self->{level}->{uncertain},
527                        line => 1, column => 1,
528                        layer => 'encode');
529      } else {
530        $self->{input_encoding} = $charset->get_iana_name;
531      }
532    
533      $self->{change_encoding} = sub {
534        my $self = shift;
535        $charset_name = shift;
536        my $token = shift;
537    
538        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
539        ($char_stream, $e_status) = $charset->get_decode_handle
540            ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
541             byte_buffer => \ $buffer->{buffer});
542        
543        if ($char_stream) { # if supported
544          ## "Change the encoding" algorithm:
545    
546          ## Step 1    
547          if ($charset->{category} &
548              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
549            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
550            ($char_stream, $e_status) = $charset->get_decode_handle
551                ($byte_stream,
552                 byte_buffer => \ $buffer->{buffer});
553          }
554          $charset_name = $charset->get_iana_name;
555          
556          ## Step 2
557          if (defined $self->{input_encoding} and
558              $self->{input_encoding} eq $charset_name) {
559            !!!parse-error (type => 'charset label:matching',
560                            text => $charset_name,
561                            level => $self->{level}->{info});
562            $self->{confident} = 1;
563            return;
564          }
565    
566          !!!parse-error (type => 'charset label detected',
567                          text => $self->{input_encoding},
568                          value => $charset_name,
569                          level => $self->{level}->{warn},
570                          token => $token);
571          
572          ## Step 3
573          # if (can) {
574            ## change the encoding on the fly.
575            #$self->{confident} = 1;
576            #return;
577          # }
578          
579          ## Step 4
580          throw Whatpm::HTML::RestartParser ();
581        }
582    }; # $self->{change_encoding}    }; # $self->{change_encoding}
583    
584    my @args = @_; shift @args; # $s    my $char_onerror = sub {
585        my (undef, $type, %opt) = @_;
586        !!!parse-error (layer => 'encode',
587                        line => $self->{line}, column => $self->{column} + 1,
588                        %opt, type => $type);
589        if ($opt{octets}) {
590          ${$opt{octets}} = "\x{FFFD}"; # relacement character
591        }
592      };
593    
594      my $wrapped_char_stream = $get_wrapper->($char_stream);
595      $wrapped_char_stream->onerror ($char_onerror);
596    
597      my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
598    my $return;    my $return;
599    try {    try {
600      $return = $self->parse_char_string ($s, @args);        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
601    } catch Whatpm::HTML::RestartParser with {    } catch Whatpm::HTML::RestartParser with {
602      my $charset = shift->{charset};      ## NOTE: Invoked after {change_encoding}.
603      $s = \ (Encode::decode ($charset, $$bytes_s));      
604      $self->{input_encoding} = $charset; ## TODO: normalize      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
605          $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
606          !!!parse-error (type => 'chardecode:fallback',
607                          level => $self->{level}->{uncertain},
608                          #text => $self->{input_encoding},
609                          line => 1, column => 1,
610                          layer => 'encode');
611        } elsif (not ($e_status &
612                      Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
613          $self->{input_encoding} = $charset->get_iana_name;
614          !!!parse-error (type => 'chardecode:no error',
615                          text => $self->{input_encoding},
616                          level => $self->{level}->{uncertain},
617                          line => 1, column => 1,
618                          layer => 'encode');
619        } else {
620          $self->{input_encoding} = $charset->get_iana_name;
621        }
622      $self->{confident} = 1;      $self->{confident} = 1;
623      $return = $self->parse_char_string ($s, @args);  
624        $wrapped_char_stream = $get_wrapper->($char_stream);
625        $wrapped_char_stream->onerror ($char_onerror);
626    
627        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
628    };    };
629    return $return;    return $return;
630  } # parse_byte_string  } # parse_byte_stream
631    
632  ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM  ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
633  ## and the HTML layer MUST ignore it.  However, we does strip BOM in  ## and the HTML layer MUST ignore it.  However, we does strip BOM in
# Line 162  sub parse_byte_string ($$$$;$) { Line 638  sub parse_byte_string ($$$$;$) {
638  ## such as |parse_byte_string| in this module, must ensure that it does  ## such as |parse_byte_string| in this module, must ensure that it does
639  ## strip the BOM and never strip any ZWNBSP.  ## strip the BOM and never strip any ZWNBSP.
640    
641  *parse_char_string = \&parse_string;  sub parse_char_string ($$$;$$) {
642      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
643      my $self = shift;
644      my $s = ref $_[0] ? $_[0] : \($_[0]);
645      require Whatpm::Charset::DecodeHandle;
646      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
647      return $self->parse_char_stream ($input, @_[1..$#_]);
648    } # parse_char_string
649    *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
650    
651  sub parse_string ($$$;$) {  sub parse_char_stream ($$$;$$) {
652    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
653    my $s = ref $_[0] ? $_[0] : \($_[0]);    my $input = $_[0];
654    $self->{document} = $_[1];    $self->{document} = $_[1];
655    @{$self->{document}->child_nodes} = ();    @{$self->{document}->child_nodes} = ();
656    
# Line 175  sub parse_string ($$$;$) { Line 659  sub parse_string ($$$;$) {
659    $self->{confident} = 1 unless exists $self->{confident};    $self->{confident} = 1 unless exists $self->{confident};
660    $self->{document}->input_encoding ($self->{input_encoding})    $self->{document}->input_encoding ($self->{input_encoding})
661        if defined $self->{input_encoding};        if defined $self->{input_encoding};
662    ## TODO: |{input_encoding}| is needless?
663    
   my $i = 0;  
664    $self->{line_prev} = $self->{line} = 1;    $self->{line_prev} = $self->{line} = 1;
665    $self->{column_prev} = $self->{column} = 0;    $self->{column_prev} = -1;
666    $self->{set_next_char} = sub {    $self->{column} = 0;
667      $self->{set_nc} = sub {
668      my $self = shift;      my $self = shift;
669    
670      pop @{$self->{prev_char}};      my $char = '';
671      unshift @{$self->{prev_char}}, $self->{next_char};      if (defined $self->{next_nc}) {
672          $char = $self->{next_nc};
673          delete $self->{next_nc};
674          $self->{nc} = ord $char;
675        } else {
676          $self->{char_buffer} = '';
677          $self->{char_buffer_pos} = 0;
678    
679      $self->{next_char} = -1 and return if $i >= length $$s;        my $count = $input->manakai_read_until
680      $self->{next_char} = ord substr $$s, $i++, 1;           ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
681          if ($count) {
682            $self->{line_prev} = $self->{line};
683            $self->{column_prev} = $self->{column};
684            $self->{column}++;
685            $self->{nc}
686                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
687            return;
688          }
689    
690          if ($input->read ($char, 1)) {
691            $self->{nc} = ord $char;
692          } else {
693            $self->{nc} = -1;
694            return;
695          }
696        }
697    
698      ($self->{line_prev}, $self->{column_prev})      ($self->{line_prev}, $self->{column_prev})
699          = ($self->{line}, $self->{column});          = ($self->{line}, $self->{column});
700      $self->{column}++;      $self->{column}++;
701            
702      if ($self->{next_char} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
703          !!!cp ('j1');
704        $self->{line}++;        $self->{line}++;
705        $self->{column} = 0;        $self->{column} = 0;
706      } elsif ($self->{next_char} == 0x000D) { # CR      } elsif ($self->{nc} == 0x000D) { # CR
707        $i++ if substr ($$s, $i, 1) eq "\x0A";        !!!cp ('j2');
708        $self->{next_char} = 0x000A; # LF # MUST  ## TODO: support for abort/streaming
709          my $next = '';
710          if ($input->read ($next, 1) and $next ne "\x0A") {
711            $self->{next_nc} = $next;
712          }
713          $self->{nc} = 0x000A; # LF # MUST
714        $self->{line}++;        $self->{line}++;
715        $self->{column} = 0;        $self->{column} = 0;
716      } elsif ($self->{next_char} > 0x10FFFF) {      } elsif ($self->{nc} == 0x0000) { # NULL
717        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        !!!cp ('j4');
     } elsif ($self->{next_char} == 0x0000) { # NULL  
718        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
719        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
720      }      }
721    };    };
722    $self->{prev_char} = [-1, -1, -1];  
723    $self->{next_char} = -1;    $self->{read_until} = sub {
724        #my ($scalar, $specials_range, $offset) = @_;
725        return 0 if defined $self->{next_nc};
726    
727        my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
728        my $offset = $_[2] || 0;
729    
730        if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
731          pos ($self->{char_buffer}) = $self->{char_buffer_pos};
732          if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
733            substr ($_[0], $offset)
734                = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
735            my $count = $+[0] - $-[0];
736            if ($count) {
737              $self->{column} += $count;
738              $self->{char_buffer_pos} += $count;
739              $self->{line_prev} = $self->{line};
740              $self->{column_prev} = $self->{column} - 1;
741              $self->{nc} = -1;
742            }
743            return $count;
744          } else {
745            return 0;
746          }
747        } else {
748          my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
749          if ($count) {
750            $self->{column} += $count;
751            $self->{line_prev} = $self->{line};
752            $self->{column_prev} = $self->{column} - 1;
753            $self->{nc} = -1;
754          }
755          return $count;
756        }
757      }; # $self->{read_until}
758    
759    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
760      my (%opt) = @_;      my (%opt) = @_;
# Line 220  sub parse_string ($$$;$) { Line 766  sub parse_string ($$$;$) {
766      $onerror->(line => $self->{line}, column => $self->{column}, @_);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
767    };    };
768    
769      my $char_onerror = sub {
770        my (undef, $type, %opt) = @_;
771        !!!parse-error (layer => 'encode',
772                        line => $self->{line}, column => $self->{column} + 1,
773                        %opt, type => $type);
774      }; # $char_onerror
775    
776      if ($_[3]) {
777        $input = $_[3]->($input);
778        $input->onerror ($char_onerror);
779      } else {
780        $input->onerror ($char_onerror) unless defined $input->onerror;
781      }
782    
783    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
784    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
785    $self->_construct_tree;    $self->_construct_tree;
# Line 228  sub parse_string ($$$;$) { Line 788  sub parse_string ($$$;$) {
788    delete $self->{parse_error}; # remove loop    delete $self->{parse_error}; # remove loop
789    
790    return $self->{document};    return $self->{document};
791  } # parse_string  } # parse_char_stream
792    
793  sub new ($) {  sub new ($) {
794    my $class = shift;    my $class = shift;
795    my $self = bless {}, $class;    my $self = bless {
796    $self->{set_next_char} = sub {      level => {must => 'm',
797      $self->{next_char} = -1;                should => 's',
798                  warn => 'w',
799                  info => 'i',
800                  uncertain => 'u'},
801      }, $class;
802      $self->{set_nc} = sub {
803        $self->{nc} = -1;
804    };    };
805    $self->{parse_error} = sub {    $self->{parse_error} = sub {
806      #      #
# Line 261  sub RCDATA_CONTENT_MODEL () { CM_ENTITY Line 827  sub RCDATA_CONTENT_MODEL () { CM_ENTITY
827  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
828    
829  sub DATA_STATE () { 0 }  sub DATA_STATE () { 0 }
830  sub ENTITY_DATA_STATE () { 1 }  #sub ENTITY_DATA_STATE () { 1 }
831  sub TAG_OPEN_STATE () { 2 }  sub TAG_OPEN_STATE () { 2 }
832  sub CLOSE_TAG_OPEN_STATE () { 3 }  sub CLOSE_TAG_OPEN_STATE () { 3 }
833  sub TAG_NAME_STATE () { 4 }  sub TAG_NAME_STATE () { 4 }
# Line 272  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 Line 838  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8
838  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
839  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
840  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
841  sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }  #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
842  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
843  sub COMMENT_START_STATE () { 14 }  sub COMMENT_START_STATE () { 14 }
844  sub COMMENT_START_DASH_STATE () { 15 }  sub COMMENT_START_DASH_STATE () { 15 }
# Line 294  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO Line 860  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO
860  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
861  sub BOGUS_DOCTYPE_STATE () { 32 }  sub BOGUS_DOCTYPE_STATE () { 32 }
862  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
863    sub SELF_CLOSING_START_TAG_STATE () { 34 }
864    sub CDATA_SECTION_STATE () { 35 }
865    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
866    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
867    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
868    sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
869    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
870    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
871    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
872    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
873    ## NOTE: "Entity data state", "entity in attribute value state", and
874    ## "consume a character reference" algorithm are jointly implemented
875    ## using the following six states:
876    sub ENTITY_STATE () { 44 }
877    sub ENTITY_HASH_STATE () { 45 }
878    sub NCR_NUM_STATE () { 46 }
879    sub HEXREF_X_STATE () { 47 }
880    sub HEXREF_HEX_STATE () { 48 }
881    sub ENTITY_NAME_STATE () { 49 }
882    sub PCDATA_STATE () { 50 } # "data state" in the spec
883    
884  sub DOCTYPE_TOKEN () { 1 }  sub DOCTYPE_TOKEN () { 1 }
885  sub COMMENT_TOKEN () { 2 }  sub COMMENT_TOKEN () { 2 }
# Line 311  sub ROW_IMS ()        { 0b10000000 } Line 897  sub ROW_IMS ()        { 0b10000000 }
897  sub BODY_AFTER_IMS () { 0b100000000 }  sub BODY_AFTER_IMS () { 0b100000000 }
898  sub FRAME_IMS ()      { 0b1000000000 }  sub FRAME_IMS ()      { 0b1000000000 }
899  sub SELECT_IMS ()     { 0b10000000000 }  sub SELECT_IMS ()     { 0b10000000000 }
900    sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
901        ## NOTE: "in foreign content" insertion mode is special; it is combined
902        ## with the secondary insertion mode.  In this parser, they are stored
903        ## together in the bit-or'ed form.
904    
905  ## NOTE: "initial" and "before html" insertion modes have no constants.  ## NOTE: "initial" and "before html" insertion modes have no constants.
906    
# Line 342  sub IN_COLUMN_GROUP_IM () { 0b10 } Line 932  sub IN_COLUMN_GROUP_IM () { 0b10 }
932  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
933    my $self = shift;    my $self = shift;
934    $self->{state} = DATA_STATE; # MUST    $self->{state} = DATA_STATE; # MUST
935      #$self->{s_kwd}; # state keyword - initialized when used
936      #$self->{entity__value}; # initialized when used
937      #$self->{entity__match}; # initialized when used
938    $self->{content_model} = PCDATA_CONTENT_MODEL; # be    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
939    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    undef $self->{ct}; # current token
940    undef $self->{current_attribute};    undef $self->{ca}; # current attribute
941    undef $self->{last_emitted_start_tag_name};    undef $self->{last_stag_name}; # last emitted start tag name
942    undef $self->{last_attribute_value_state};    #$self->{prev_state}; # initialized when used
943    $self->{char} = [];    delete $self->{self_closing};
944    # $self->{next_char}    $self->{char_buffer} = '';
945      $self->{char_buffer_pos} = 0;
946      $self->{nc} = -1; # next input character
947      #$self->{next_nc}
948    !!!next-input-character;    !!!next-input-character;
949    $self->{token} = [];    $self->{token} = [];
950    # $self->{escape}    # $self->{escape}
# Line 359  sub _initialize_tokenizer ($) { Line 955  sub _initialize_tokenizer ($) {
955  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
956  ##   ->{name} (DOCTYPE_TOKEN)  ##   ->{name} (DOCTYPE_TOKEN)
957  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
958  ##   ->{public_identifier} (DOCTYPE_TOKEN)  ##   ->{pubid} (DOCTYPE_TOKEN)
959  ##   ->{system_identifier} (DOCTYPE_TOKEN)  ##   ->{sysid} (DOCTYPE_TOKEN)
960  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
961  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
962  ##        ->{name}  ##        ->{name}
963  ##        ->{value}  ##        ->{value}
964  ##        ->{has_reference} == 1 or 0  ##        ->{has_reference} == 1 or 0
965  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
966    ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
967    ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
968    ##     while the token is pushed back to the stack.
969    
970  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
971    
# Line 376  sub _initialize_tokenizer ($) { Line 975  sub _initialize_tokenizer ($) {
975  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
976  ## and removed from the list.  ## and removed from the list.
977    
978  ## NOTE: HTML5 "Writing HTML documents" section, applied to  ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
979  ## documents and not to user agents and conformance checkers,  ## (This requirement was dropped from HTML5 spec, unfortunately.)
980  ## contains some requirements that are not detected by the  
981  ## parsing algorithm:  my $is_space = {
982  ## - Some requirements on character encoding declarations. ## TODO    0x0009 => 1, # CHARACTER TABULATION (HT)
983  ## - "Elements MUST NOT contain content that their content model disallows."    0x000A => 1, # LINE FEED (LF)
984  ##   ... Some are parse error, some are not (will be reported by c.c.).    #0x000B => 0, # LINE TABULATION (VT)
985  ## - Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) ## TODO    0x000C => 1, # FORM FEED (FF)
986  ## - Text (in elements, attributes, and comments) SHOULD NOT contain    #0x000D => 1, # CARRIAGE RETURN (CR)
987  ##   control characters other than space characters. ## TODO: (what is control character? C0, C1 and DEL?  Unicode control character?)    0x0020 => 1, # SPACE (SP)
988    };
 ## TODO: HTML5 poses authors two SHOULD-level requirements that cannot  
 ## be detected by the HTML5 parsing algorithm:  
 ## - Text,  
989    
990  sub _get_next_token ($) {  sub _get_next_token ($) {
991    my $self = shift;    my $self = shift;
992    
993      if ($self->{self_closing}) {
994        !!!parse-error (type => 'nestc', token => $self->{ct});
995        ## NOTE: The |self_closing| flag is only set by start tag token.
996        ## In addition, when a start tag token is emitted, it is always set to
997        ## |ct|.
998        delete $self->{self_closing};
999      }
1000    
1001    if (@{$self->{token}}) {    if (@{$self->{token}}) {
1002        $self->{self_closing} = $self->{token}->[0]->{self_closing};
1003      return shift @{$self->{token}};      return shift @{$self->{token}};
1004    }    }
1005    
1006    A: {    A: {
1007      if ($self->{state} == DATA_STATE) {      if ($self->{state} == PCDATA_STATE) {
1008        if ($self->{next_char} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1009    
1010          if ($self->{nc} == 0x0026) { # &
1011            !!!cp (0.1);
1012            ## NOTE: In the spec, the tokenizer is switched to the
1013            ## "entity data state".  In this implementation, the tokenizer
1014            ## is switched to the |ENTITY_STATE|, which is an implementation
1015            ## of the "consume a character reference" algorithm.
1016            $self->{entity_add} = -1;
1017            $self->{prev_state} = DATA_STATE;
1018            $self->{state} = ENTITY_STATE;
1019            !!!next-input-character;
1020            redo A;
1021          } elsif ($self->{nc} == 0x003C) { # <
1022            !!!cp (0.2);
1023            $self->{state} = TAG_OPEN_STATE;
1024            !!!next-input-character;
1025            redo A;
1026          } elsif ($self->{nc} == -1) {
1027            !!!cp (0.3);
1028            !!!emit ({type => END_OF_FILE_TOKEN,
1029                      line => $self->{line}, column => $self->{column}});
1030            last A; ## TODO: ok?
1031          } else {
1032            !!!cp (0.4);
1033            #
1034          }
1035    
1036          # Anything else
1037          my $token = {type => CHARACTER_TOKEN,
1038                       data => chr $self->{nc},
1039                       line => $self->{line}, column => $self->{column},
1040                      };
1041          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1042    
1043          ## Stay in the state.
1044          !!!next-input-character;
1045          !!!emit ($token);
1046          redo A;
1047        } elsif ($self->{state} == DATA_STATE) {
1048          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1049          if ($self->{nc} == 0x0026) { # &
1050            $self->{s_kwd} = '';
1051          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1052              not $self->{escape}) {              not $self->{escape}) {
1053            !!!cp (1);            !!!cp (1);
1054            $self->{state} = ENTITY_DATA_STATE;            ## NOTE: In the spec, the tokenizer is switched to the
1055              ## "entity data state".  In this implementation, the tokenizer
1056              ## is switched to the |ENTITY_STATE|, which is an implementation
1057              ## of the "consume a character reference" algorithm.
1058              $self->{entity_add} = -1;
1059              $self->{prev_state} = DATA_STATE;
1060              $self->{state} = ENTITY_STATE;
1061            !!!next-input-character;            !!!next-input-character;
1062            redo A;            redo A;
1063          } else {          } else {
1064            !!!cp (2);            !!!cp (2);
1065            #            #
1066          }          }
1067        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
1068          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1069            unless ($self->{escape}) {            $self->{s_kwd} .= '-';
1070              if ($self->{prev_char}->[0] == 0x002D and # -            
1071                  $self->{prev_char}->[1] == 0x0021 and # !            if ($self->{s_kwd} eq '<!--') {
1072                  $self->{prev_char}->[2] == 0x003C) { # <              !!!cp (3);
1073                !!!cp (3);              $self->{escape} = 1; # unless $self->{escape};
1074                $self->{escape} = 1;              $self->{s_kwd} = '--';
1075              } else {              #
1076                !!!cp (4);            } elsif ($self->{s_kwd} eq '---') {
1077              }              !!!cp (4);
1078                $self->{s_kwd} = '--';
1079                #
1080            } else {            } else {
1081              !!!cp (5);              !!!cp (5);
1082                #
1083            }            }
1084          }          }
1085                    
1086          #          #
1087        } elsif ($self->{next_char} == 0x003C) { # <        } elsif ($self->{nc} == 0x0021) { # !
1088            if (length $self->{s_kwd}) {
1089              !!!cp (5.1);
1090              $self->{s_kwd} .= '!';
1091              #
1092            } else {
1093              !!!cp (5.2);
1094              #$self->{s_kwd} = '';
1095              #
1096            }
1097            #
1098          } elsif ($self->{nc} == 0x003C) { # <
1099          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1100              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1101               not $self->{escape})) {               not $self->{escape})) {
# Line 437  sub _get_next_token ($) { Line 1105  sub _get_next_token ($) {
1105            redo A;            redo A;
1106          } else {          } else {
1107            !!!cp (7);            !!!cp (7);
1108              $self->{s_kwd} = '';
1109            #            #
1110          }          }
1111        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1112          if ($self->{escape} and          if ($self->{escape} and
1113              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1114            if ($self->{prev_char}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '--') {
               $self->{prev_char}->[1] == 0x002D) { # -  
1115              !!!cp (8);              !!!cp (8);
1116              delete $self->{escape};              delete $self->{escape};
1117            } else {            } else {
# Line 453  sub _get_next_token ($) { Line 1121  sub _get_next_token ($) {
1121            !!!cp (10);            !!!cp (10);
1122          }          }
1123                    
1124            $self->{s_kwd} = '';
1125          #          #
1126        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1127          !!!cp (11);          !!!cp (11);
1128            $self->{s_kwd} = '';
1129          !!!emit ({type => END_OF_FILE_TOKEN,          !!!emit ({type => END_OF_FILE_TOKEN,
1130                    line => $self->{line}, column => $self->{column}});                    line => $self->{line}, column => $self->{column}});
1131          last A; ## TODO: ok?          last A; ## TODO: ok?
1132        } else {        } else {
1133          !!!cp (12);          !!!cp (12);
1134            $self->{s_kwd} = '';
1135            #
1136        }        }
1137    
1138        # Anything else        # Anything else
1139        my $token = {type => CHARACTER_TOKEN,        my $token = {type => CHARACTER_TOKEN,
1140                     data => chr $self->{next_char},                     data => chr $self->{nc},
1141                     line => $self->{line}, column => $self->{column}};                     line => $self->{line}, column => $self->{column},
1142        ## Stay in the data state                    };
1143        !!!next-input-character;        if ($self->{read_until}->($token->{data}, q[-!<>&],
1144                                    length $token->{data})) {
1145        !!!emit ($token);          $self->{s_kwd} = '';
1146          }
       redo A;  
     } elsif ($self->{state} == ENTITY_DATA_STATE) {  
       ## (cannot happen in CDATA state)  
   
       my ($l, $c) = ($self->{line_prev}, $self->{column_prev});  
         
       my $token = $self->_tokenize_attempt_to_consume_an_entity (0, -1);  
   
       $self->{state} = DATA_STATE;  
       # next-input-character is already done  
1147    
1148        unless (defined $token) {        ## Stay in the data state.
1149          if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1150          !!!cp (13);          !!!cp (13);
1151          !!!emit ({type => CHARACTER_TOKEN, data => '&',          $self->{state} = PCDATA_STATE;
                   line => $l, column => $c});  
1152        } else {        } else {
1153          !!!cp (14);          !!!cp (14);
1154          !!!emit ($token);          ## Stay in the state.
1155        }        }
1156          !!!next-input-character;
1157          !!!emit ($token);
1158        redo A;        redo A;
1159      } elsif ($self->{state} == TAG_OPEN_STATE) {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1160        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1161          if ($self->{next_char} == 0x002F) { # /          if ($self->{nc} == 0x002F) { # /
1162            !!!cp (15);            !!!cp (15);
1163            !!!next-input-character;            !!!next-input-character;
1164            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1165            redo A;            redo A;
1166            } elsif ($self->{nc} == 0x0021) { # !
1167              !!!cp (15.1);
1168              $self->{s_kwd} = '<' unless $self->{escape};
1169              #
1170          } else {          } else {
1171            !!!cp (16);            !!!cp (16);
1172            ## reconsume            #
           $self->{state} = DATA_STATE;  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev}});  
   
           redo A;  
1173          }          }
1174    
1175            ## reconsume
1176            $self->{state} = DATA_STATE;
1177            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1178                      line => $self->{line_prev},
1179                      column => $self->{column_prev},
1180                     });
1181            redo A;
1182        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1183          if ($self->{next_char} == 0x0021) { # !          if ($self->{nc} == 0x0021) { # !
1184            !!!cp (17);            !!!cp (17);
1185            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1186            !!!next-input-character;            !!!next-input-character;
1187            redo A;            redo A;
1188          } elsif ($self->{next_char} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1189            !!!cp (18);            !!!cp (18);
1190            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1191            !!!next-input-character;            !!!next-input-character;
1192            redo A;            redo A;
1193          } elsif (0x0041 <= $self->{next_char} and          } elsif (0x0041 <= $self->{nc} and
1194                   $self->{next_char} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1195            !!!cp (19);            !!!cp (19);
1196            $self->{current_token}            $self->{ct}
1197              = {type => START_TAG_TOKEN,              = {type => START_TAG_TOKEN,
1198                 tag_name => chr ($self->{next_char} + 0x0020),                 tag_name => chr ($self->{nc} + 0x0020),
1199                 line => $self->{line_prev},                 line => $self->{line_prev},
1200                 column => $self->{column_prev}};                 column => $self->{column_prev}};
1201            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1202            !!!next-input-character;            !!!next-input-character;
1203            redo A;            redo A;
1204          } elsif (0x0061 <= $self->{next_char} and          } elsif (0x0061 <= $self->{nc} and
1205                   $self->{next_char} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1206            !!!cp (20);            !!!cp (20);
1207            $self->{current_token} = {type => START_TAG_TOKEN,            $self->{ct} = {type => START_TAG_TOKEN,
1208                                      tag_name => chr ($self->{next_char}),                                      tag_name => chr ($self->{nc}),
1209                                      line => $self->{line_prev},                                      line => $self->{line_prev},
1210                                      column => $self->{column_prev}};                                      column => $self->{column_prev}};
1211            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1212            !!!next-input-character;            !!!next-input-character;
1213            redo A;            redo A;
1214          } elsif ($self->{next_char} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1215            !!!cp (21);            !!!cp (21);
1216            !!!parse-error (type => 'empty start tag');            !!!parse-error (type => 'empty start tag',
1217                              line => $self->{line_prev},
1218                              column => $self->{column_prev});
1219            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1220            !!!next-input-character;            !!!next-input-character;
1221    
1222            !!!emit ({type => CHARACTER_TOKEN, data => '<>',            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
1223                      line => $self->{line_prev},                      line => $self->{line_prev},
1224                      column => $self->{column_prev}});                      column => $self->{column_prev},
1225                       });
1226    
1227            redo A;            redo A;
1228          } elsif ($self->{next_char} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1229            !!!cp (22);            !!!cp (22);
1230            !!!parse-error (type => 'pio');            !!!parse-error (type => 'pio',
1231                              line => $self->{line_prev},
1232                              column => $self->{column_prev});
1233            $self->{state} = BOGUS_COMMENT_STATE;            $self->{state} = BOGUS_COMMENT_STATE;
1234            $self->{current_token} = {type => COMMENT_TOKEN, data => '',            $self->{ct} = {type => COMMENT_TOKEN, data => '',
1235                                      line => $self->{line_prev},                                      line => $self->{line_prev},
1236                                      column => $self->{column_prev}};                                      column => $self->{column_prev},
1237            ## $self->{next_char} is intentionally left as is                                     };
1238              ## $self->{nc} is intentionally left as is
1239            redo A;            redo A;
1240          } else {          } else {
1241            !!!cp (23);            !!!cp (23);
1242            !!!parse-error (type => 'bare stago');            !!!parse-error (type => 'bare stago',
1243                              line => $self->{line_prev},
1244                              column => $self->{column_prev});
1245            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1246            ## reconsume            ## reconsume
1247    
1248            !!!emit ({type => CHARACTER_TOKEN, data => '<',            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1249                      line => $self->{line_prev},                      line => $self->{line_prev},
1250                      column => $self->{column_prev}});                      column => $self->{column_prev},
1251                       });
1252    
1253            redo A;            redo A;
1254          }          }
# Line 578  sub _get_next_token ($) { Line 1256  sub _get_next_token ($) {
1256          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
1257        }        }
1258      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1259          ## NOTE: The "close tag open state" in the spec is implemented as
1260          ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
1261    
1262        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1263        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1264          if (defined $self->{last_emitted_start_tag_name}) {          if (defined $self->{last_stag_name}) {
1265              $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1266            ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>            $self->{s_kwd} = '';
1267            my @next_char;            ## Reconsume.
1268            TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {            redo A;
             push @next_char, $self->{next_char};  
             my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);  
             my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;  
             if ($self->{next_char} == $c or $self->{next_char} == $C) {  
               !!!cp (24);  
               !!!next-input-character;  
               next TAGNAME;  
             } else {  
               !!!cp (25);  
               $self->{next_char} = shift @next_char; # reconsume  
               !!!back-next-input-character (@next_char);  
               $self->{state} = DATA_STATE;  
   
               !!!emit ({type => CHARACTER_TOKEN, data => '</',  
                         line => $l, column => $c});  
     
               redo A;  
             }  
           }  
           push @next_char, $self->{next_char};  
         
           unless ($self->{next_char} == 0x0009 or # HT  
                   $self->{next_char} == 0x000A or # LF  
                   $self->{next_char} == 0x000B or # VT  
                   $self->{next_char} == 0x000C or # FF  
                   $self->{next_char} == 0x0020 or # SP  
                   $self->{next_char} == 0x003E or # >  
                   $self->{next_char} == 0x002F or # /  
                   $self->{next_char} == -1) {  
             !!!cp (26);  
             $self->{next_char} = shift @next_char; # reconsume  
             !!!back-next-input-character (@next_char);  
             $self->{state} = DATA_STATE;  
             !!!emit ({type => CHARACTER_TOKEN, data => '</',  
                       line => $l, column => $c});  
             redo A;  
           } else {  
             !!!cp (27);  
             $self->{next_char} = shift @next_char;  
             !!!back-next-input-character (@next_char);  
             # and consume...  
           }  
1269          } else {          } else {
1270            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1271              ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1272            !!!cp (28);            !!!cp (28);
           # next-input-character is already done  
1273            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1274              ## Reconsume.
1275            !!!emit ({type => CHARACTER_TOKEN, data => '</',            !!!emit ({type => CHARACTER_TOKEN, data => '</',
1276                      line => $l, column => $c});                      line => $l, column => $c,
1277                       });
1278            redo A;            redo A;
1279          }          }
1280        }        }
1281          
1282        if (0x0041 <= $self->{next_char} and        if (0x0041 <= $self->{nc} and
1283            $self->{next_char} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1284          !!!cp (29);          !!!cp (29);
1285          $self->{current_token}          $self->{ct}
1286              = {type => END_TAG_TOKEN,              = {type => END_TAG_TOKEN,
1287                 tag_name => chr ($self->{next_char} + 0x0020),                 tag_name => chr ($self->{nc} + 0x0020),
1288                 line => $l, column => $c};                 line => $l, column => $c};
1289          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1290          !!!next-input-character;          !!!next-input-character;
1291          redo A;          redo A;
1292        } elsif (0x0061 <= $self->{next_char} and        } elsif (0x0061 <= $self->{nc} and
1293                 $self->{next_char} <= 0x007A) { # a..z                 $self->{nc} <= 0x007A) { # a..z
1294          !!!cp (30);          !!!cp (30);
1295          $self->{current_token} = {type => END_TAG_TOKEN,          $self->{ct} = {type => END_TAG_TOKEN,
1296                                    tag_name => chr ($self->{next_char}),                                    tag_name => chr ($self->{nc}),
1297                                    line => $l, column => $c};                                    line => $l, column => $c};
1298          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1299          !!!next-input-character;          !!!next-input-character;
1300          redo A;          redo A;
1301        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1302          !!!cp (31);          !!!cp (31);
1303          !!!parse-error (type => 'empty end tag');          !!!parse-error (type => 'empty end tag',
1304                            line => $self->{line_prev}, ## "<" in "</>"
1305                            column => $self->{column_prev} - 1);
1306          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1307          !!!next-input-character;          !!!next-input-character;
1308          redo A;          redo A;
1309        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1310          !!!cp (32);          !!!cp (32);
1311          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1312          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1313          # reconsume          # reconsume
1314    
1315          !!!emit ({type => CHARACTER_TOKEN, data => '</',          !!!emit ({type => CHARACTER_TOKEN, data => '</',
1316                    line => $l, column => $c});                    line => $l, column => $c,
1317                     });
1318    
1319          redo A;          redo A;
1320        } else {        } else {
1321          !!!cp (33);          !!!cp (33);
1322          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1323          $self->{state} = BOGUS_COMMENT_STATE;          $self->{state} = BOGUS_COMMENT_STATE;
1324          $self->{current_token} = {type => COMMENT_TOKEN, data => '',          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1325                                    line => $self->{line_prev}, # "<" of "</"                                    line => $self->{line_prev}, # "<" of "</"
1326                                    column => $self->{column_prev} - 1};                                    column => $self->{column_prev} - 1,
1327          ## $self->{next_char} is intentionally left as is                                   };
1328          redo A;          ## NOTE: $self->{nc} is intentionally left as is.
1329            ## Although the "anything else" case of the spec not explicitly
1330            ## states that the next input character is to be reconsumed,
1331            ## it will be included to the |data| of the comment token
1332            ## generated from the bogus end tag, as defined in the
1333            ## "bogus comment state" entry.
1334            redo A;
1335          }
1336        } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1337          my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1338          if (length $ch) {
1339            my $CH = $ch;
1340            $ch =~ tr/a-z/A-Z/;
1341            my $nch = chr $self->{nc};
1342            if ($nch eq $ch or $nch eq $CH) {
1343              !!!cp (24);
1344              ## Stay in the state.
1345              $self->{s_kwd} .= $nch;
1346              !!!next-input-character;
1347              redo A;
1348            } else {
1349              !!!cp (25);
1350              $self->{state} = DATA_STATE;
1351              ## Reconsume.
1352              !!!emit ({type => CHARACTER_TOKEN,
1353                        data => '</' . $self->{s_kwd},
1354                        line => $self->{line_prev},
1355                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1356                       });
1357              redo A;
1358            }
1359          } else { # after "<{tag-name}"
1360            unless ($is_space->{$self->{nc}} or
1361                    {
1362                     0x003E => 1, # >
1363                     0x002F => 1, # /
1364                     -1 => 1, # EOF
1365                    }->{$self->{nc}}) {
1366              !!!cp (26);
1367              ## Reconsume.
1368              $self->{state} = DATA_STATE;
1369              !!!emit ({type => CHARACTER_TOKEN,
1370                        data => '</' . $self->{s_kwd},
1371                        line => $self->{line_prev},
1372                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1373                       });
1374              redo A;
1375            } else {
1376              !!!cp (27);
1377              $self->{ct}
1378                  = {type => END_TAG_TOKEN,
1379                     tag_name => $self->{last_stag_name},
1380                     line => $self->{line_prev},
1381                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1382              $self->{state} = TAG_NAME_STATE;
1383              ## Reconsume.
1384              redo A;
1385            }
1386        }        }
1387      } elsif ($self->{state} == TAG_NAME_STATE) {      } elsif ($self->{state} == TAG_NAME_STATE) {
1388        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1389          !!!cp (34);          !!!cp (34);
1390          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1391          !!!next-input-character;          !!!next-input-character;
1392          redo A;          redo A;
1393        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1394          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1395            !!!cp (35);            !!!cp (35);
1396            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1397                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1398            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1399            #if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1400            #  ## NOTE: This should never be reached.            #  ## NOTE: This should never be reached.
1401            #  !!! cp (36);            #  !!! cp (36);
1402            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 709  sub _get_next_token ($) { Line 1404  sub _get_next_token ($) {
1404              !!!cp (37);              !!!cp (37);
1405            #}            #}
1406          } else {          } else {
1407            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1408          }          }
1409          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1410          !!!next-input-character;          !!!next-input-character;
1411    
1412          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1413    
1414          redo A;          redo A;
1415        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1416                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1417          !!!cp (38);          !!!cp (38);
1418          $self->{current_token}->{tag_name} .= chr ($self->{next_char} + 0x0020);          $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1419            # start tag or end tag            # start tag or end tag
1420          ## Stay in this state          ## Stay in this state
1421          !!!next-input-character;          !!!next-input-character;
1422          redo A;          redo A;
1423        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1424          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1425          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1426            !!!cp (39);            !!!cp (39);
1427            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1428                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
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            #  ## NOTE: This state should never be reached.            #  ## NOTE: This state should never be reached.
1432            #  !!! cp (40);            #  !!! cp (40);
1433            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 742  sub _get_next_token ($) { Line 1435  sub _get_next_token ($) {
1435              !!!cp (41);              !!!cp (41);
1436            #}            #}
1437          } else {          } else {
1438            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1439          }          }
1440          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1441          # reconsume          # reconsume
1442    
1443          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1444    
1445          redo A;          redo A;
1446        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1447            !!!cp (42);
1448            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1449          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_char} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           !!!cp (42);  
           #  
         } else {  
           !!!cp (43);  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
1450          redo A;          redo A;
1451        } else {        } else {
1452          !!!cp (44);          !!!cp (44);
1453          $self->{current_token}->{tag_name} .= chr $self->{next_char};          $self->{ct}->{tag_name} .= chr $self->{nc};
1454            # start tag or end tag            # start tag or end tag
1455          ## Stay in the state          ## Stay in the state
1456          !!!next-input-character;          !!!next-input-character;
1457          redo A;          redo A;
1458        }        }
1459      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1460        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1461          !!!cp (45);          !!!cp (45);
1462          ## Stay in the state          ## Stay in the state
1463          !!!next-input-character;          !!!next-input-character;
1464          redo A;          redo A;
1465        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1466          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1467            !!!cp (46);            !!!cp (46);
1468            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1469                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1470            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1471            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1472              !!!cp (47);              !!!cp (47);
1473              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1474            } else {            } else {
1475              !!!cp (48);              !!!cp (48);
1476            }            }
1477          } else {          } else {
1478            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1479          }          }
1480          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1481          !!!next-input-character;          !!!next-input-character;
1482    
1483          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1484    
1485          redo A;          redo A;
1486        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1487                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1488          !!!cp (49);          !!!cp (49);
1489          $self->{current_attribute} = {name => chr ($self->{next_char} + 0x0020),          $self->{ca}
1490                                value => ''};              = {name => chr ($self->{nc} + 0x0020),
1491                   value => '',
1492                   line => $self->{line}, column => $self->{column}};
1493          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1494          !!!next-input-character;          !!!next-input-character;
1495          redo A;          redo A;
1496        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1497            !!!cp (50);
1498            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1499          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_char} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           !!!cp (50);  
           #  
         } else {  
           !!!cp (51);  
           !!!parse-error (type => 'nestc');  
         }  
         ## Stay in the state  
         # next-input-character is already done  
1500          redo A;          redo A;
1501        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1502          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1503          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1504            !!!cp (52);            !!!cp (52);
1505            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1506                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1507            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1508            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1509              !!!cp (53);              !!!cp (53);
1510              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1511            } else {            } else {
1512              !!!cp (54);              !!!cp (54);
1513            }            }
1514          } else {          } else {
1515            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1516          }          }
1517          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1518          # reconsume          # reconsume
1519    
1520          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1521    
1522          redo A;          redo A;
1523        } else {        } else {
# Line 858  sub _get_next_token ($) { Line 1525  sub _get_next_token ($) {
1525               0x0022 => 1, # "               0x0022 => 1, # "
1526               0x0027 => 1, # '               0x0027 => 1, # '
1527               0x003D => 1, # =               0x003D => 1, # =
1528              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1529            !!!cp (55);            !!!cp (55);
1530            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1531          } else {          } else {
1532            !!!cp (56);            !!!cp (56);
1533          }          }
1534          $self->{current_attribute} = {name => chr ($self->{next_char}),          $self->{ca}
1535                                value => ''};              = {name => chr ($self->{nc}),
1536                   value => '',
1537                   line => $self->{line}, column => $self->{column}};
1538          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1539          !!!next-input-character;          !!!next-input-character;
1540          redo A;          redo A;
1541        }        }
1542      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1543        my $before_leave = sub {        my $before_leave = sub {
1544          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1545              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1546            !!!cp (57);            !!!cp (57);
1547            !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name});            !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1548            ## Discard $self->{current_attribute} # MUST            ## Discard $self->{ca} # MUST
1549          } else {          } else {
1550            !!!cp (58);            !!!cp (58);
1551            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            $self->{ct}->{attributes}->{$self->{ca}->{name}}
1552              = $self->{current_attribute};              = $self->{ca};
1553          }          }
1554        }; # $before_leave        }; # $before_leave
1555    
1556        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1557          !!!cp (59);          !!!cp (59);
1558          $before_leave->();          $before_leave->();
1559          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1560          !!!next-input-character;          !!!next-input-character;
1561          redo A;          redo A;
1562        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1563          !!!cp (60);          !!!cp (60);
1564          $before_leave->();          $before_leave->();
1565          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1566          !!!next-input-character;          !!!next-input-character;
1567          redo A;          redo A;
1568        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1569          $before_leave->();          $before_leave->();
1570          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1571            !!!cp (61);            !!!cp (61);
1572            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1573                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1574            !!!cp (62);            !!!cp (62);
1575            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1576            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1577              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1578            }            }
1579          } else {          } else {
1580            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1581          }          }
1582          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1583          !!!next-input-character;          !!!next-input-character;
1584    
1585          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1586    
1587          redo A;          redo A;
1588        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1589                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1590          !!!cp (63);          !!!cp (63);
1591          $self->{current_attribute}->{name} .= chr ($self->{next_char} + 0x0020);          $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1592          ## Stay in the state          ## Stay in the state
1593          !!!next-input-character;          !!!next-input-character;
1594          redo A;          redo A;
1595        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1596            !!!cp (64);
1597          $before_leave->();          $before_leave->();
1598            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1599          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_char} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           !!!cp (64);  
           #  
         } else {  
           !!!cp (65);  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
1600          redo A;          redo A;
1601        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1602          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1603          $before_leave->();          $before_leave->();
1604          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1605            !!!cp (66);            !!!cp (66);
1606            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1607                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1608            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1609            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1610              !!!cp (67);              !!!cp (67);
1611              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1612            } else {            } else {
# Line 963  sub _get_next_token ($) { Line 1614  sub _get_next_token ($) {
1614              !!!cp (68);              !!!cp (68);
1615            }            }
1616          } else {          } else {
1617            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1618          }          }
1619          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1620          # reconsume          # reconsume
1621    
1622          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1623    
1624          redo A;          redo A;
1625        } else {        } else {
1626          if ($self->{next_char} == 0x0022 or # "          if ($self->{nc} == 0x0022 or # "
1627              $self->{next_char} == 0x0027) { # '              $self->{nc} == 0x0027) { # '
1628            !!!cp (69);            !!!cp (69);
1629            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1630          } else {          } else {
1631            !!!cp (70);            !!!cp (70);
1632          }          }
1633          $self->{current_attribute}->{name} .= chr ($self->{next_char});          $self->{ca}->{name} .= chr ($self->{nc});
1634          ## Stay in the state          ## Stay in the state
1635          !!!next-input-character;          !!!next-input-character;
1636          redo A;          redo A;
1637        }        }
1638      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1639        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1640          !!!cp (71);          !!!cp (71);
1641          ## Stay in the state          ## Stay in the state
1642          !!!next-input-character;          !!!next-input-character;
1643          redo A;          redo A;
1644        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1645          !!!cp (72);          !!!cp (72);
1646          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1647          !!!next-input-character;          !!!next-input-character;
1648          redo A;          redo A;
1649        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1650          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1651            !!!cp (73);            !!!cp (73);
1652            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1653                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1654            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1655            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1656              !!!cp (74);              !!!cp (74);
1657              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1658            } else {            } else {
# Line 1015  sub _get_next_token ($) { Line 1660  sub _get_next_token ($) {
1660              !!!cp (75);              !!!cp (75);
1661            }            }
1662          } else {          } else {
1663            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1664          }          }
1665          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1666          !!!next-input-character;          !!!next-input-character;
1667    
1668          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1669    
1670          redo A;          redo A;
1671        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1672                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1673          !!!cp (76);          !!!cp (76);
1674          $self->{current_attribute} = {name => chr ($self->{next_char} + 0x0020),          $self->{ca}
1675                                value => ''};              = {name => chr ($self->{nc} + 0x0020),
1676                   value => '',
1677                   line => $self->{line}, column => $self->{column}};
1678          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1679          !!!next-input-character;          !!!next-input-character;
1680          redo A;          redo A;
1681        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1682            !!!cp (77);
1683            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1684          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_char} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           !!!cp (77);  
           #  
         } else {  
           !!!cp (78);  
           !!!parse-error (type => 'nestc');  
           ## TODO: Different error type for <aa / bb> than <aa/>  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
1685          redo A;          redo A;
1686        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1687          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1688          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1689            !!!cp (79);            !!!cp (79);
1690            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1691                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1692            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1693            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1694              !!!cp (80);              !!!cp (80);
1695              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1696            } else {            } else {
# Line 1064  sub _get_next_token ($) { Line 1698  sub _get_next_token ($) {
1698              !!!cp (81);              !!!cp (81);
1699            }            }
1700          } else {          } else {
1701            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1702          }          }
1703          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1704          # reconsume          # reconsume
1705    
1706          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1707    
1708          redo A;          redo A;
1709        } else {        } else {
1710          !!!cp (82);          if ($self->{nc} == 0x0022 or # "
1711          $self->{current_attribute} = {name => chr ($self->{next_char}),              $self->{nc} == 0x0027) { # '
1712                                value => ''};            !!!cp (78);
1713              !!!parse-error (type => 'bad attribute name');
1714            } else {
1715              !!!cp (82);
1716            }
1717            $self->{ca}
1718                = {name => chr ($self->{nc}),
1719                   value => '',
1720                   line => $self->{line}, column => $self->{column}};
1721          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1722          !!!next-input-character;          !!!next-input-character;
1723          redo A;                  redo A;        
1724        }        }
1725      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1726        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP        
1727          !!!cp (83);          !!!cp (83);
1728          ## Stay in the state          ## Stay in the state
1729          !!!next-input-character;          !!!next-input-character;
1730          redo A;          redo A;
1731        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1732          !!!cp (84);          !!!cp (84);
1733          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1734          !!!next-input-character;          !!!next-input-character;
1735          redo A;          redo A;
1736        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1737          !!!cp (85);          !!!cp (85);
1738          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1739          ## reconsume          ## reconsume
1740          redo A;          redo A;
1741        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1742          !!!cp (86);          !!!cp (86);
1743          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1744          !!!next-input-character;          !!!next-input-character;
1745          redo A;          redo A;
1746        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1747          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          !!!parse-error (type => 'empty unquoted attribute value');
1748            if ($self->{ct}->{type} == START_TAG_TOKEN) {
1749            !!!cp (87);            !!!cp (87);
1750            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1751                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1752            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1753            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1754              !!!cp (88);              !!!cp (88);
1755              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1756            } else {            } else {
# Line 1121  sub _get_next_token ($) { Line 1758  sub _get_next_token ($) {
1758              !!!cp (89);              !!!cp (89);
1759            }            }
1760          } else {          } else {
1761            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1762          }          }
1763          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1764          !!!next-input-character;          !!!next-input-character;
1765    
1766          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1767    
1768          redo A;          redo A;
1769        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1770          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1771          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1772            !!!cp (90);            !!!cp (90);
1773            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1774                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1775            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1776            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1777              !!!cp (91);              !!!cp (91);
1778              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1779            } else {            } else {
# Line 1146  sub _get_next_token ($) { Line 1781  sub _get_next_token ($) {
1781              !!!cp (92);              !!!cp (92);
1782            }            }
1783          } else {          } else {
1784            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1785          }          }
1786          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1787          ## reconsume          ## reconsume
1788    
1789          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1790    
1791          redo A;          redo A;
1792        } else {        } else {
1793          if ($self->{next_char} == 0x003D) { # =          if ($self->{nc} == 0x003D) { # =
1794            !!!cp (93);            !!!cp (93);
1795            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1796          } else {          } else {
1797            !!!cp (94);            !!!cp (94);
1798          }          }
1799          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1800          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1801          !!!next-input-character;          !!!next-input-character;
1802          redo A;          redo A;
1803        }        }
1804      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1805        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1806          !!!cp (95);          !!!cp (95);
1807          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1808          !!!next-input-character;          !!!next-input-character;
1809          redo A;          redo A;
1810        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1811          !!!cp (96);          !!!cp (96);
1812          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1813          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1814            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1815            ## implementation of the "consume a character reference" algorithm.
1816            $self->{prev_state} = $self->{state};
1817            $self->{entity_add} = 0x0022; # "
1818            $self->{state} = ENTITY_STATE;
1819          !!!next-input-character;          !!!next-input-character;
1820          redo A;          redo A;
1821        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1822          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1823          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1824            !!!cp (97);            !!!cp (97);
1825            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1826                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1827            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1828            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1829              !!!cp (98);              !!!cp (98);
1830              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1831            } else {            } else {
# Line 1195  sub _get_next_token ($) { Line 1833  sub _get_next_token ($) {
1833              !!!cp (99);              !!!cp (99);
1834            }            }
1835          } else {          } else {
1836            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1837          }          }
1838          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1839          ## reconsume          ## reconsume
1840    
1841          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1842    
1843          redo A;          redo A;
1844        } else {        } else {
1845          !!!cp (100);          !!!cp (100);
1846          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1847            $self->{read_until}->($self->{ca}->{value},
1848                                  q["&],
1849                                  length $self->{ca}->{value});
1850    
1851          ## Stay in the state          ## Stay in the state
1852          !!!next-input-character;          !!!next-input-character;
1853          redo A;          redo A;
1854        }        }
1855      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1856        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1857          !!!cp (101);          !!!cp (101);
1858          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1859          !!!next-input-character;          !!!next-input-character;
1860          redo A;          redo A;
1861        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1862          !!!cp (102);          !!!cp (102);
1863          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1864          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1865            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1866            ## implementation of the "consume a character reference" algorithm.
1867            $self->{entity_add} = 0x0027; # '
1868            $self->{prev_state} = $self->{state};
1869            $self->{state} = ENTITY_STATE;
1870          !!!next-input-character;          !!!next-input-character;
1871          redo A;          redo A;
1872        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1873          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1874          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1875            !!!cp (103);            !!!cp (103);
1876            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1877                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1878            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1879            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1880              !!!cp (104);              !!!cp (104);
1881              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1882            } else {            } else {
# Line 1239  sub _get_next_token ($) { Line 1884  sub _get_next_token ($) {
1884              !!!cp (105);              !!!cp (105);
1885            }            }
1886          } else {          } else {
1887            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1888          }          }
1889          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1890          ## reconsume          ## reconsume
1891    
1892          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1893    
1894          redo A;          redo A;
1895        } else {        } else {
1896          !!!cp (106);          !!!cp (106);
1897          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1898            $self->{read_until}->($self->{ca}->{value},
1899                                  q['&],
1900                                  length $self->{ca}->{value});
1901    
1902          ## Stay in the state          ## Stay in the state
1903          !!!next-input-character;          !!!next-input-character;
1904          redo A;          redo A;
1905        }        }
1906      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1907        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # HT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1908          !!!cp (107);          !!!cp (107);
1909          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1910          !!!next-input-character;          !!!next-input-character;
1911          redo A;          redo A;
1912        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1913          !!!cp (108);          !!!cp (108);
1914          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1915          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1916            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1917            ## implementation of the "consume a character reference" algorithm.
1918            $self->{entity_add} = -1;
1919            $self->{prev_state} = $self->{state};
1920            $self->{state} = ENTITY_STATE;
1921          !!!next-input-character;          !!!next-input-character;
1922          redo A;          redo A;
1923        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1924          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1925            !!!cp (109);            !!!cp (109);
1926            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1927                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1928            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1929            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1930              !!!cp (110);              !!!cp (110);
1931              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1932            } else {            } else {
# Line 1286  sub _get_next_token ($) { Line 1934  sub _get_next_token ($) {
1934              !!!cp (111);              !!!cp (111);
1935            }            }
1936          } else {          } else {
1937            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1938          }          }
1939          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1940          !!!next-input-character;          !!!next-input-character;
1941    
1942          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1943    
1944          redo A;          redo A;
1945        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1946          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1947          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1948            !!!cp (112);            !!!cp (112);
1949            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1950                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1951            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1952            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1953              !!!cp (113);              !!!cp (113);
1954              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1955            } else {            } else {
# Line 1311  sub _get_next_token ($) { Line 1957  sub _get_next_token ($) {
1957              !!!cp (114);              !!!cp (114);
1958            }            }
1959          } else {          } else {
1960            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1961          }          }
1962          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1963          ## reconsume          ## reconsume
1964    
1965          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1966    
1967          redo A;          redo A;
1968        } else {        } else {
# Line 1324  sub _get_next_token ($) { Line 1970  sub _get_next_token ($) {
1970               0x0022 => 1, # "               0x0022 => 1, # "
1971               0x0027 => 1, # '               0x0027 => 1, # '
1972               0x003D => 1, # =               0x003D => 1, # =
1973              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1974            !!!cp (115);            !!!cp (115);
1975            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1976          } else {          } else {
1977            !!!cp (116);            !!!cp (116);
1978          }          }
1979          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1980            $self->{read_until}->($self->{ca}->{value},
1981                                  q["'=& >],
1982                                  length $self->{ca}->{value});
1983    
1984          ## Stay in the state          ## Stay in the state
1985          !!!next-input-character;          !!!next-input-character;
1986          redo A;          redo A;
1987        }        }
     } elsif ($self->{state} == ENTITY_IN_ATTRIBUTE_VALUE_STATE) {  
       my $token = $self->_tokenize_attempt_to_consume_an_entity  
           (1,  
            $self->{last_attribute_value_state}  
              == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE ? 0x0022 : # "  
            $self->{last_attribute_value_state}  
              == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE ? 0x0027 : # '  
            -1);  
   
       unless (defined $token) {  
         !!!cp (117);  
         $self->{current_attribute}->{value} .= '&';  
       } else {  
         !!!cp (118);  
         $self->{current_attribute}->{value} .= $token->{data};  
         $self->{current_attribute}->{has_reference} = $token->{has_reference};  
         ## ISSUE: spec says "append the returned character token to the current attribute's value"  
       }  
   
       $self->{state} = $self->{last_attribute_value_state};  
       # next-input-character is already done  
       redo A;  
1988      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
1989        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1990          !!!cp (118);          !!!cp (118);
1991          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1992          !!!next-input-character;          !!!next-input-character;
1993          redo A;          redo A;
1994        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1995          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1996            !!!cp (119);            !!!cp (119);
1997            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1998                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1999            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2000            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2001              !!!cp (120);              !!!cp (120);
2002              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2003            } else {            } else {
# Line 1383  sub _get_next_token ($) { Line 2005  sub _get_next_token ($) {
2005              !!!cp (121);              !!!cp (121);
2006            }            }
2007          } else {          } else {
2008            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
2009          }          }
2010          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2011          !!!next-input-character;          !!!next-input-character;
2012    
2013          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2014    
2015          redo A;          redo A;
2016        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
2017            !!!cp (122);
2018            $self->{state} = SELF_CLOSING_START_TAG_STATE;
2019          !!!next-input-character;          !!!next-input-character;
2020          if ($self->{next_char} == 0x003E and # >          redo A;
2021              $self->{current_token}->{type} == START_TAG_TOKEN and        } elsif ($self->{nc} == -1) {
2022              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {          !!!parse-error (type => 'unclosed tag');
2023            # permitted slash          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2024            !!!cp (122);            !!!cp (122.3);
2025            #            $self->{last_stag_name} = $self->{ct}->{tag_name};
2026            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2027              if ($self->{ct}->{attributes}) {
2028                !!!cp (122.1);
2029                !!!parse-error (type => 'end tag attribute');
2030              } else {
2031                ## NOTE: This state should never be reached.
2032                !!!cp (122.2);
2033              }
2034          } else {          } else {
2035            !!!cp (123);            die "$0: $self->{ct}->{type}: Unknown token type";
           !!!parse-error (type => 'nestc');  
2036          }          }
2037          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = DATA_STATE;
2038          # next-input-character is already done          ## Reconsume.
2039            !!!emit ($self->{ct}); # start tag or end tag
2040          redo A;          redo A;
2041        } else {        } else {
2042          !!!cp (124);          !!!cp ('124.1');
2043          !!!parse-error (type => 'no space between attributes');          !!!parse-error (type => 'no space between attributes');
2044          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2045          ## reconsume          ## reconsume
2046          redo A;          redo A;
2047        }        }
2048      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {      } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2049        ## (only happen if PCDATA state)        if ($self->{nc} == 0x003E) { # >
2050                  if ($self->{ct}->{type} == END_TAG_TOKEN) {
2051        ## NOTE: Set by the previous state            !!!cp ('124.2');
2052        #my $token = {type => COMMENT_TOKEN, data => ''};            !!!parse-error (type => 'nestc', token => $self->{ct});
2053              ## TODO: Different type than slash in start tag
2054        BC: {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2055          if ($self->{next_char} == 0x003E) { # >            if ($self->{ct}->{attributes}) {
2056            !!!cp (124);              !!!cp ('124.4');
2057            $self->{state} = DATA_STATE;              !!!parse-error (type => 'end tag attribute');
2058            !!!next-input-character;            } else {
2059                !!!cp ('124.5');
2060            !!!emit ($self->{current_token}); # comment            }
2061              ## TODO: Test |<title></title/>|
2062            } else {
2063              !!!cp ('124.3');
2064              $self->{self_closing} = 1;
2065            }
2066    
2067            redo A;          $self->{state} = DATA_STATE;
2068          } elsif ($self->{next_char} == -1) {          !!!next-input-character;
           !!!cp (125);  
           $self->{state} = DATA_STATE;  
           ## reconsume  
2069    
2070            !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # start tag or end tag
2071    
2072            redo A;          redo A;
2073          } elsif ($self->{nc} == -1) {
2074            !!!parse-error (type => 'unclosed tag');
2075            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2076              !!!cp (124.7);
2077              $self->{last_stag_name} = $self->{ct}->{tag_name};
2078            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2079              if ($self->{ct}->{attributes}) {
2080                !!!cp (124.5);
2081                !!!parse-error (type => 'end tag attribute');
2082              } else {
2083                ## NOTE: This state should never be reached.
2084                !!!cp (124.6);
2085              }
2086          } else {          } else {
2087            !!!cp (126);            die "$0: $self->{ct}->{type}: Unknown token type";
           $self->{current_token}->{data} .= chr ($self->{next_char}); # comment  
           !!!next-input-character;  
           redo BC;  
2088          }          }
2089        } # BC          $self->{state} = DATA_STATE;
2090            ## Reconsume.
2091        die "$0: _get_next_token: unexpected case [BC]";          !!!emit ($self->{ct}); # start tag or end tag
2092      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {          redo A;
2093          } else {
2094            !!!cp ('124.4');
2095            !!!parse-error (type => 'nestc');
2096            ## TODO: This error type is wrong.
2097            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2098            ## Reconsume.
2099            redo A;
2100          }
2101        } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2102        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
2103    
2104        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1);        ## NOTE: Unlike spec's "bogus comment state", this implementation
2105          ## consumes characters one-by-one basis.
       my @next_char;  
       push @next_char, $self->{next_char};  
2106                
2107        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x003E) { # >
2108            !!!cp (124);
2109            $self->{state} = DATA_STATE;
2110          !!!next-input-character;          !!!next-input-character;
2111          push @next_char, $self->{next_char};  
2112          if ($self->{next_char} == 0x002D) { # -          !!!emit ($self->{ct}); # comment
2113            !!!cp (127);          redo A;
2114            $self->{current_token} = {type => COMMENT_TOKEN, data => '',        } elsif ($self->{nc} == -1) {
2115                                      line => $l, column => $c};          !!!cp (125);
2116            $self->{state} = COMMENT_START_STATE;          $self->{state} = DATA_STATE;
2117            !!!next-input-character;          ## reconsume
2118            redo A;  
2119          } else {          !!!emit ($self->{ct}); # comment
2120            !!!cp (128);          redo A;
2121          }        } else {
2122        } elsif ($self->{next_char} == 0x0044 or # D          !!!cp (126);
2123                 $self->{next_char} == 0x0064) { # d          $self->{ct}->{data} .= chr ($self->{nc}); # comment
2124            $self->{read_until}->($self->{ct}->{data},
2125                                  q[>],
2126                                  length $self->{ct}->{data});
2127    
2128            ## Stay in the state.
2129          !!!next-input-character;          !!!next-input-character;
2130          push @next_char, $self->{next_char};          redo A;
2131          if ($self->{next_char} == 0x004F or # O        }
2132              $self->{next_char} == 0x006F) { # o      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2133            !!!next-input-character;        ## (only happen if PCDATA state)
2134            push @next_char, $self->{next_char};        
2135            if ($self->{next_char} == 0x0043 or # C        if ($self->{nc} == 0x002D) { # -
2136                $self->{next_char} == 0x0063) { # c          !!!cp (133);
2137              !!!next-input-character;          $self->{state} = MD_HYPHEN_STATE;
2138              push @next_char, $self->{next_char};          !!!next-input-character;
2139              if ($self->{next_char} == 0x0054 or # T          redo A;
2140                  $self->{next_char} == 0x0074) { # t        } elsif ($self->{nc} == 0x0044 or # D
2141                !!!next-input-character;                 $self->{nc} == 0x0064) { # d
2142                push @next_char, $self->{next_char};          ## ASCII case-insensitive.
2143                if ($self->{next_char} == 0x0059 or # Y          !!!cp (130);
2144                    $self->{next_char} == 0x0079) { # y          $self->{state} = MD_DOCTYPE_STATE;
2145                  !!!next-input-character;          $self->{s_kwd} = chr $self->{nc};
2146                  push @next_char, $self->{next_char};          !!!next-input-character;
2147                  if ($self->{next_char} == 0x0050 or # P          redo A;
2148                      $self->{next_char} == 0x0070) { # p        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2149                    !!!next-input-character;                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2150                    push @next_char, $self->{next_char};                 $self->{nc} == 0x005B) { # [
2151                    if ($self->{next_char} == 0x0045 or # E          !!!cp (135.4);                
2152                        $self->{next_char} == 0x0065) { # e          $self->{state} = MD_CDATA_STATE;
2153                      !!!cp (129);          $self->{s_kwd} = '[';
2154                      ## TODO: What a stupid code this is!          !!!next-input-character;
2155                      $self->{state} = DOCTYPE_STATE;          redo A;
                     $self->{current_token} = {type => DOCTYPE_TOKEN,  
                                               quirks => 1,  
                                               line => $l, column => $c};  
                     !!!next-input-character;  
                     redo A;  
                   } else {  
                     !!!cp (130);  
                   }  
                 } else {  
                   !!!cp (131);  
                 }  
               } else {  
                 !!!cp (132);  
               }  
             } else {  
               !!!cp (133);  
             }  
           } else {  
             !!!cp (134);  
           }  
         } else {  
           !!!cp (135);  
         }  
2156        } else {        } else {
2157          !!!cp (136);          !!!cp (136);
2158        }        }
2159    
2160        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment',
2161        $self->{next_char} = shift @next_char;                        line => $self->{line_prev},
2162        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2163          ## Reconsume.
2164        $self->{state} = BOGUS_COMMENT_STATE;        $self->{state} = BOGUS_COMMENT_STATE;
2165        $self->{current_token} = {type => COMMENT_TOKEN, data => '',        $self->{ct} = {type => COMMENT_TOKEN, data => '',
2166                                  line => $l, column => $c};                                  line => $self->{line_prev},
2167                                    column => $self->{column_prev} - 1,
2168                                   };
2169        redo A;        redo A;
2170              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2171        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2172        ## ISSUE: spec is somewhat unclear on "is the first character that will be in the comment"; what is "that will be in the comment" is what the algorithm defines, isn't it?          !!!cp (127);
2173            $self->{ct} = {type => COMMENT_TOKEN, data => '',
2174                                      line => $self->{line_prev},
2175                                      column => $self->{column_prev} - 2,
2176                                     };
2177            $self->{state} = COMMENT_START_STATE;
2178            !!!next-input-character;
2179            redo A;
2180          } else {
2181            !!!cp (128);
2182            !!!parse-error (type => 'bogus comment',
2183                            line => $self->{line_prev},
2184                            column => $self->{column_prev} - 2);
2185            $self->{state} = BOGUS_COMMENT_STATE;
2186            ## Reconsume.
2187            $self->{ct} = {type => COMMENT_TOKEN,
2188                                      data => '-',
2189                                      line => $self->{line_prev},
2190                                      column => $self->{column_prev} - 2,
2191                                     };
2192            redo A;
2193          }
2194        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2195          ## ASCII case-insensitive.
2196          if ($self->{nc} == [
2197                undef,
2198                0x004F, # O
2199                0x0043, # C
2200                0x0054, # T
2201                0x0059, # Y
2202                0x0050, # P
2203              ]->[length $self->{s_kwd}] or
2204              $self->{nc} == [
2205                undef,
2206                0x006F, # o
2207                0x0063, # c
2208                0x0074, # t
2209                0x0079, # y
2210                0x0070, # p
2211              ]->[length $self->{s_kwd}]) {
2212            !!!cp (131);
2213            ## Stay in the state.
2214            $self->{s_kwd} .= chr $self->{nc};
2215            !!!next-input-character;
2216            redo A;
2217          } elsif ((length $self->{s_kwd}) == 6 and
2218                   ($self->{nc} == 0x0045 or # E
2219                    $self->{nc} == 0x0065)) { # e
2220            !!!cp (129);
2221            $self->{state} = DOCTYPE_STATE;
2222            $self->{ct} = {type => DOCTYPE_TOKEN,
2223                                      quirks => 1,
2224                                      line => $self->{line_prev},
2225                                      column => $self->{column_prev} - 7,
2226                                     };
2227            !!!next-input-character;
2228            redo A;
2229          } else {
2230            !!!cp (132);        
2231            !!!parse-error (type => 'bogus comment',
2232                            line => $self->{line_prev},
2233                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2234            $self->{state} = BOGUS_COMMENT_STATE;
2235            ## Reconsume.
2236            $self->{ct} = {type => COMMENT_TOKEN,
2237                                      data => $self->{s_kwd},
2238                                      line => $self->{line_prev},
2239                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2240                                     };
2241            redo A;
2242          }
2243        } elsif ($self->{state} == MD_CDATA_STATE) {
2244          if ($self->{nc} == {
2245                '[' => 0x0043, # C
2246                '[C' => 0x0044, # D
2247                '[CD' => 0x0041, # A
2248                '[CDA' => 0x0054, # T
2249                '[CDAT' => 0x0041, # A
2250              }->{$self->{s_kwd}}) {
2251            !!!cp (135.1);
2252            ## Stay in the state.
2253            $self->{s_kwd} .= chr $self->{nc};
2254            !!!next-input-character;
2255            redo A;
2256          } elsif ($self->{s_kwd} eq '[CDATA' and
2257                   $self->{nc} == 0x005B) { # [
2258            !!!cp (135.2);
2259            $self->{ct} = {type => CHARACTER_TOKEN,
2260                                      data => '',
2261                                      line => $self->{line_prev},
2262                                      column => $self->{column_prev} - 7};
2263            $self->{state} = CDATA_SECTION_STATE;
2264            !!!next-input-character;
2265            redo A;
2266          } else {
2267            !!!cp (135.3);
2268            !!!parse-error (type => 'bogus comment',
2269                            line => $self->{line_prev},
2270                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2271            $self->{state} = BOGUS_COMMENT_STATE;
2272            ## Reconsume.
2273            $self->{ct} = {type => COMMENT_TOKEN,
2274                                      data => $self->{s_kwd},
2275                                      line => $self->{line_prev},
2276                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2277                                     };
2278            redo A;
2279          }
2280      } elsif ($self->{state} == COMMENT_START_STATE) {      } elsif ($self->{state} == COMMENT_START_STATE) {
2281        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2282          !!!cp (137);          !!!cp (137);
2283          $self->{state} = COMMENT_START_DASH_STATE;          $self->{state} = COMMENT_START_DASH_STATE;
2284          !!!next-input-character;          !!!next-input-character;
2285          redo A;          redo A;
2286        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2287          !!!cp (138);          !!!cp (138);
2288          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2289          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2290          !!!next-input-character;          !!!next-input-character;
2291    
2292          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2293    
2294          redo A;          redo A;
2295        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2296          !!!cp (139);          !!!cp (139);
2297          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2298          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2299          ## reconsume          ## reconsume
2300    
2301          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2302    
2303          redo A;          redo A;
2304        } else {        } else {
2305          !!!cp (140);          !!!cp (140);
2306          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2307              .= chr ($self->{next_char});              .= chr ($self->{nc});
2308          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2309          !!!next-input-character;          !!!next-input-character;
2310          redo A;          redo A;
2311        }        }
2312      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2313        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2314          !!!cp (141);          !!!cp (141);
2315          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2316          !!!next-input-character;          !!!next-input-character;
2317          redo A;          redo A;
2318        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2319          !!!cp (142);          !!!cp (142);
2320          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2321          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2322          !!!next-input-character;          !!!next-input-character;
2323    
2324          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2325    
2326          redo A;          redo A;
2327        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2328          !!!cp (143);          !!!cp (143);
2329          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2330          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2331          ## reconsume          ## reconsume
2332    
2333          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2334    
2335          redo A;          redo A;
2336        } else {        } else {
2337          !!!cp (144);          !!!cp (144);
2338          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2339              .= '-' . chr ($self->{next_char});              .= '-' . chr ($self->{nc});
2340          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2341          !!!next-input-character;          !!!next-input-character;
2342          redo A;          redo A;
2343        }        }
2344      } elsif ($self->{state} == COMMENT_STATE) {      } elsif ($self->{state} == COMMENT_STATE) {
2345        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2346          !!!cp (145);          !!!cp (145);
2347          $self->{state} = COMMENT_END_DASH_STATE;          $self->{state} = COMMENT_END_DASH_STATE;
2348          !!!next-input-character;          !!!next-input-character;
2349          redo A;          redo A;
2350        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2351          !!!cp (146);          !!!cp (146);
2352          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2353          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2354          ## reconsume          ## reconsume
2355    
2356          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2357    
2358          redo A;          redo A;
2359        } else {        } else {
2360          !!!cp (147);          !!!cp (147);
2361          $self->{current_token}->{data} .= chr ($self->{next_char}); # comment          $self->{ct}->{data} .= chr ($self->{nc}); # comment
2362            $self->{read_until}->($self->{ct}->{data},
2363                                  q[-],
2364                                  length $self->{ct}->{data});
2365    
2366          ## Stay in the state          ## Stay in the state
2367          !!!next-input-character;          !!!next-input-character;
2368          redo A;          redo A;
2369        }        }
2370      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2371        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2372          !!!cp (148);          !!!cp (148);
2373          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2374          !!!next-input-character;          !!!next-input-character;
2375          redo A;          redo A;
2376        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2377          !!!cp (149);          !!!cp (149);
2378          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2379          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2380          ## reconsume          ## reconsume
2381    
2382          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2383    
2384          redo A;          redo A;
2385        } else {        } else {
2386          !!!cp (150);          !!!cp (150);
2387          $self->{current_token}->{data} .= '-' . chr ($self->{next_char}); # comment          $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2388          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2389          !!!next-input-character;          !!!next-input-character;
2390          redo A;          redo A;
2391        }        }
2392      } elsif ($self->{state} == COMMENT_END_STATE) {      } elsif ($self->{state} == COMMENT_END_STATE) {
2393        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2394          !!!cp (151);          !!!cp (151);
2395          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2396          !!!next-input-character;          !!!next-input-character;
2397    
2398          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2399    
2400          redo A;          redo A;
2401        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2402          !!!cp (152);          !!!cp (152);
2403          !!!parse-error (type => 'dash in comment');          !!!parse-error (type => 'dash in comment',
2404          $self->{current_token}->{data} .= '-'; # comment                          line => $self->{line_prev},
2405                            column => $self->{column_prev});
2406            $self->{ct}->{data} .= '-'; # comment
2407          ## Stay in the state          ## Stay in the state
2408          !!!next-input-character;          !!!next-input-character;
2409          redo A;          redo A;
2410        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2411          !!!cp (153);          !!!cp (153);
2412          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2413          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2414          ## reconsume          ## reconsume
2415    
2416          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2417    
2418          redo A;          redo A;
2419        } else {        } else {
2420          !!!cp (154);          !!!cp (154);
2421          !!!parse-error (type => 'dash in comment');          !!!parse-error (type => 'dash in comment',
2422          $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment                          line => $self->{line_prev},
2423                            column => $self->{column_prev});
2424            $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2425          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2426          !!!next-input-character;          !!!next-input-character;
2427          redo A;          redo A;
2428        }        }
2429      } elsif ($self->{state} == DOCTYPE_STATE) {      } elsif ($self->{state} == DOCTYPE_STATE) {
2430        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2431          !!!cp (155);          !!!cp (155);
2432          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2433          !!!next-input-character;          !!!next-input-character;
# Line 1691  sub _get_next_token ($) { Line 2440  sub _get_next_token ($) {
2440          redo A;          redo A;
2441        }        }
2442      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2443        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2444          !!!cp (157);          !!!cp (157);
2445          ## Stay in the state          ## Stay in the state
2446          !!!next-input-character;          !!!next-input-character;
2447          redo A;          redo A;
2448        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2449          !!!cp (158);          !!!cp (158);
2450          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2451          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2452          !!!next-input-character;          !!!next-input-character;
2453    
2454          !!!emit ($self->{current_token}); # DOCTYPE (quirks)          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2455    
2456          redo A;          redo A;
2457        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2458          !!!cp (159);          !!!cp (159);
2459          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2460          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2461          ## reconsume          ## reconsume
2462    
2463          !!!emit ($self->{current_token}); # DOCTYPE (quirks)          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2464    
2465          redo A;          redo A;
2466        } else {        } else {
2467          !!!cp (160);          !!!cp (160);
2468          $self->{current_token}->{name} = chr $self->{next_char};          $self->{ct}->{name} = chr $self->{nc};
2469          delete $self->{current_token}->{quirks};          delete $self->{ct}->{quirks};
2470  ## ISSUE: "Set the token's name name to the" in the spec  ## ISSUE: "Set the token's name name to the" in the spec
2471          $self->{state} = DOCTYPE_NAME_STATE;          $self->{state} = DOCTYPE_NAME_STATE;
2472          !!!next-input-character;          !!!next-input-character;
# Line 1729  sub _get_next_token ($) { Line 2474  sub _get_next_token ($) {
2474        }        }
2475      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2476  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2477        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2478          !!!cp (161);          !!!cp (161);
2479          $self->{state} = AFTER_DOCTYPE_NAME_STATE;          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
2480          !!!next-input-character;          !!!next-input-character;
2481          redo A;          redo A;
2482        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2483          !!!cp (162);          !!!cp (162);
2484          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2485          !!!next-input-character;          !!!next-input-character;
2486    
2487          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2488    
2489          redo A;          redo A;
2490        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2491          !!!cp (163);          !!!cp (163);
2492          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2493          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2494          ## reconsume          ## reconsume
2495    
2496          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2497          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2498    
2499          redo A;          redo A;
2500        } else {        } else {
2501          !!!cp (164);          !!!cp (164);
2502          $self->{current_token}->{name}          $self->{ct}->{name}
2503            .= chr ($self->{next_char}); # DOCTYPE            .= chr ($self->{nc}); # DOCTYPE
2504          ## Stay in the state          ## Stay in the state
2505          !!!next-input-character;          !!!next-input-character;
2506          redo A;          redo A;
2507        }        }
2508      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2509        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2510          !!!cp (165);          !!!cp (165);
2511          ## Stay in the state          ## Stay in the state
2512          !!!next-input-character;          !!!next-input-character;
2513          redo A;          redo A;
2514        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2515          !!!cp (166);          !!!cp (166);
2516          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2517          !!!next-input-character;          !!!next-input-character;
2518    
2519          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2520    
2521          redo A;          redo A;
2522        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2523          !!!cp (167);          !!!cp (167);
2524          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2525          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2526          ## reconsume          ## reconsume
2527    
2528          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2529          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2530    
2531          redo A;          redo A;
2532        } elsif ($self->{next_char} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2533                 $self->{next_char} == 0x0070) { # p                 $self->{nc} == 0x0070) { # p
2534            $self->{state} = PUBLIC_STATE;
2535            $self->{s_kwd} = chr $self->{nc};
2536          !!!next-input-character;          !!!next-input-character;
2537          if ($self->{next_char} == 0x0055 or # U          redo A;
2538              $self->{next_char} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2539            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2540            if ($self->{next_char} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2541                $self->{next_char} == 0x0062) { # b          $self->{s_kwd} = chr $self->{nc};
             !!!next-input-character;  
             if ($self->{next_char} == 0x004C or # L  
                 $self->{next_char} == 0x006C) { # l  
               !!!next-input-character;  
               if ($self->{next_char} == 0x0049 or # I  
                   $self->{next_char} == 0x0069) { # i  
                 !!!next-input-character;  
                 if ($self->{next_char} == 0x0043 or # C  
                     $self->{next_char} == 0x0063) { # c  
                   !!!cp (168);  
                   $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
                   !!!next-input-character;  
                   redo A;  
                 } else {  
                   !!!cp (169);  
                 }  
               } else {  
                 !!!cp (170);  
               }  
             } else {  
               !!!cp (171);  
             }  
           } else {  
             !!!cp (172);  
           }  
         } else {  
           !!!cp (173);  
         }  
   
         #  
       } elsif ($self->{next_char} == 0x0053 or # S  
                $self->{next_char} == 0x0073) { # s  
2542          !!!next-input-character;          !!!next-input-character;
2543          if ($self->{next_char} == 0x0059 or # Y          redo A;
             $self->{next_char} == 0x0079) { # y  
           !!!next-input-character;  
           if ($self->{next_char} == 0x0053 or # S  
               $self->{next_char} == 0x0073) { # s  
             !!!next-input-character;  
             if ($self->{next_char} == 0x0054 or # T  
                 $self->{next_char} == 0x0074) { # t  
               !!!next-input-character;  
               if ($self->{next_char} == 0x0045 or # E  
                   $self->{next_char} == 0x0065) { # e  
                 !!!next-input-character;  
                 if ($self->{next_char} == 0x004D or # M  
                     $self->{next_char} == 0x006D) { # m  
                   !!!cp (174);  
                   $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
                   !!!next-input-character;  
                   redo A;  
                 } else {  
                   !!!cp (175);  
                 }  
               } else {  
                 !!!cp (176);  
               }  
             } else {  
               !!!cp (177);  
             }  
           } else {  
             !!!cp (178);  
           }  
         } else {  
           !!!cp (179);  
         }  
   
         #  
2544        } else {        } else {
2545          !!!cp (180);          !!!cp (180);
2546            !!!parse-error (type => 'string after DOCTYPE name');
2547            $self->{ct}->{quirks} = 1;
2548    
2549            $self->{state} = BOGUS_DOCTYPE_STATE;
2550          !!!next-input-character;          !!!next-input-character;
2551          #          redo A;
2552        }        }
2553        } elsif ($self->{state} == PUBLIC_STATE) {
2554          ## ASCII case-insensitive
2555          if ($self->{nc} == [
2556                undef,
2557                0x0055, # U
2558                0x0042, # B
2559                0x004C, # L
2560                0x0049, # I
2561              ]->[length $self->{s_kwd}] or
2562              $self->{nc} == [
2563                undef,
2564                0x0075, # u
2565                0x0062, # b
2566                0x006C, # l
2567                0x0069, # i
2568              ]->[length $self->{s_kwd}]) {
2569            !!!cp (175);
2570            ## Stay in the state.
2571            $self->{s_kwd} .= chr $self->{nc};
2572            !!!next-input-character;
2573            redo A;
2574          } elsif ((length $self->{s_kwd}) == 5 and
2575                   ($self->{nc} == 0x0043 or # C
2576                    $self->{nc} == 0x0063)) { # c
2577            !!!cp (168);
2578            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2579            !!!next-input-character;
2580            redo A;
2581          } else {
2582            !!!cp (169);
2583            !!!parse-error (type => 'string after DOCTYPE name',
2584                            line => $self->{line_prev},
2585                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2586            $self->{ct}->{quirks} = 1;
2587    
2588        !!!parse-error (type => 'string after DOCTYPE name');          $self->{state} = BOGUS_DOCTYPE_STATE;
2589        $self->{current_token}->{quirks} = 1;          ## Reconsume.
2590            redo A;
2591          }
2592        } elsif ($self->{state} == SYSTEM_STATE) {
2593          ## ASCII case-insensitive
2594          if ($self->{nc} == [
2595                undef,
2596                0x0059, # Y
2597                0x0053, # S
2598                0x0054, # T
2599                0x0045, # E
2600              ]->[length $self->{s_kwd}] or
2601              $self->{nc} == [
2602                undef,
2603                0x0079, # y
2604                0x0073, # s
2605                0x0074, # t
2606                0x0065, # e
2607              ]->[length $self->{s_kwd}]) {
2608            !!!cp (170);
2609            ## Stay in the state.
2610            $self->{s_kwd} .= chr $self->{nc};
2611            !!!next-input-character;
2612            redo A;
2613          } elsif ((length $self->{s_kwd}) == 5 and
2614                   ($self->{nc} == 0x004D or # M
2615                    $self->{nc} == 0x006D)) { # m
2616            !!!cp (171);
2617            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2618            !!!next-input-character;
2619            redo A;
2620          } else {
2621            !!!cp (172);
2622            !!!parse-error (type => 'string after DOCTYPE name',
2623                            line => $self->{line_prev},
2624                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2625            $self->{ct}->{quirks} = 1;
2626    
2627        $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2628        # next-input-character is already done          ## Reconsume.
2629        redo A;          redo A;
2630          }
2631      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2632        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2633          !!!cp (181);          !!!cp (181);
2634          ## Stay in the state          ## Stay in the state
2635          !!!next-input-character;          !!!next-input-character;
2636          redo A;          redo A;
2637        } elsif ($self->{next_char} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2638          !!!cp (182);          !!!cp (182);
2639          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2640          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2641          !!!next-input-character;          !!!next-input-character;
2642          redo A;          redo A;
2643        } elsif ($self->{next_char} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2644          !!!cp (183);          !!!cp (183);
2645          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2646          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2647          !!!next-input-character;          !!!next-input-character;
2648          redo A;          redo A;
2649        } elsif ($self->{next_char} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2650          !!!cp (184);          !!!cp (184);
2651          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2652    
2653          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2654          !!!next-input-character;          !!!next-input-character;
2655    
2656          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2657          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2658    
2659          redo A;          redo A;
2660        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2661          !!!cp (185);          !!!cp (185);
2662          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2663    
2664          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2665          ## reconsume          ## reconsume
2666    
2667          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2668          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2669    
2670          redo A;          redo A;
2671        } else {        } else {
2672          !!!cp (186);          !!!cp (186);
2673          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2674          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2675    
2676          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2677          !!!next-input-character;          !!!next-input-character;
2678          redo A;          redo A;
2679        }        }
2680      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2681        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2682          !!!cp (187);          !!!cp (187);
2683          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2684          !!!next-input-character;          !!!next-input-character;
2685          redo A;          redo A;
2686        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2687          !!!cp (188);          !!!cp (188);
2688          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2689    
2690          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2691          !!!next-input-character;          !!!next-input-character;
2692    
2693          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2694          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2695    
2696          redo A;          redo A;
2697        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2698          !!!cp (189);          !!!cp (189);
2699          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2700    
2701          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2702          ## reconsume          ## reconsume
2703    
2704          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2705          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2706    
2707          redo A;          redo A;
2708        } else {        } else {
2709          !!!cp (190);          !!!cp (190);
2710          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2711              .= chr $self->{next_char};              .= chr $self->{nc};
2712            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2713                                  length $self->{ct}->{pubid});
2714    
2715          ## Stay in the state          ## Stay in the state
2716          !!!next-input-character;          !!!next-input-character;
2717          redo A;          redo A;
2718        }        }
2719      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2720        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2721          !!!cp (191);          !!!cp (191);
2722          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2723          !!!next-input-character;          !!!next-input-character;
2724          redo A;          redo A;
2725        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2726          !!!cp (192);          !!!cp (192);
2727          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2728    
2729          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2730          !!!next-input-character;          !!!next-input-character;
2731    
2732          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2733          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2734    
2735          redo A;          redo A;
2736        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2737          !!!cp (193);          !!!cp (193);
2738          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2739    
2740          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2741          ## reconsume          ## reconsume
2742    
2743          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2744          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2745    
2746          redo A;          redo A;
2747        } else {        } else {
2748          !!!cp (194);          !!!cp (194);
2749          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2750              .= chr $self->{next_char};              .= chr $self->{nc};
2751            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2752                                  length $self->{ct}->{pubid});
2753    
2754          ## Stay in the state          ## Stay in the state
2755          !!!next-input-character;          !!!next-input-character;
2756          redo A;          redo A;
2757        }        }
2758      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2759        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2760          !!!cp (195);          !!!cp (195);
2761          ## Stay in the state          ## Stay in the state
2762          !!!next-input-character;          !!!next-input-character;
2763          redo A;          redo A;
2764        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2765          !!!cp (196);          !!!cp (196);
2766          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2767          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2768          !!!next-input-character;          !!!next-input-character;
2769          redo A;          redo A;
2770        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2771          !!!cp (197);          !!!cp (197);
2772          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2773          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2774          !!!next-input-character;          !!!next-input-character;
2775          redo A;          redo A;
2776        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2777          !!!cp (198);          !!!cp (198);
2778          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2779          !!!next-input-character;          !!!next-input-character;
2780    
2781          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2782    
2783          redo A;          redo A;
2784        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2785          !!!cp (199);          !!!cp (199);
2786          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2787    
2788          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2789          ## reconsume          ## reconsume
2790    
2791          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2792          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2793    
2794          redo A;          redo A;
2795        } else {        } else {
2796          !!!cp (200);          !!!cp (200);
2797          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2798          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2799    
2800          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2801          !!!next-input-character;          !!!next-input-character;
2802          redo A;          redo A;
2803        }        }
2804      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2805        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2806          !!!cp (201);          !!!cp (201);
2807          ## Stay in the state          ## Stay in the state
2808          !!!next-input-character;          !!!next-input-character;
2809          redo A;          redo A;
2810        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2811          !!!cp (202);          !!!cp (202);
2812          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2813          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2814          !!!next-input-character;          !!!next-input-character;
2815          redo A;          redo A;
2816        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2817          !!!cp (203);          !!!cp (203);
2818          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2819          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2820          !!!next-input-character;          !!!next-input-character;
2821          redo A;          redo A;
2822        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2823          !!!cp (204);          !!!cp (204);
2824          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2825          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2826          !!!next-input-character;          !!!next-input-character;
2827    
2828          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2829          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2830    
2831          redo A;          redo A;
2832        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2833          !!!cp (205);          !!!cp (205);
2834          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2835    
2836          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2837          ## reconsume          ## reconsume
2838    
2839          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2840          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2841    
2842          redo A;          redo A;
2843        } else {        } else {
2844          !!!cp (206);          !!!cp (206);
2845          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2846          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2847    
2848          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2849          !!!next-input-character;          !!!next-input-character;
2850          redo A;          redo A;
2851        }        }
2852      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2853        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2854          !!!cp (207);          !!!cp (207);
2855          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2856          !!!next-input-character;          !!!next-input-character;
2857          redo A;          redo A;
2858        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2859          !!!cp (208);          !!!cp (208);
2860          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2861    
2862          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2863          !!!next-input-character;          !!!next-input-character;
2864    
2865          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2866          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2867    
2868          redo A;          redo A;
2869        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2870          !!!cp (209);          !!!cp (209);
2871          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2872    
2873          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2874          ## reconsume          ## reconsume
2875    
2876          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2877          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2878    
2879          redo A;          redo A;
2880        } else {        } else {
2881          !!!cp (210);          !!!cp (210);
2882          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2883              .= chr $self->{next_char};              .= chr $self->{nc};
2884            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2885                                  length $self->{ct}->{sysid});
2886    
2887          ## Stay in the state          ## Stay in the state
2888          !!!next-input-character;          !!!next-input-character;
2889          redo A;          redo A;
2890        }        }
2891      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2892        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2893          !!!cp (211);          !!!cp (211);
2894          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2895          !!!next-input-character;          !!!next-input-character;
2896          redo A;          redo A;
2897        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2898          !!!cp (212);          !!!cp (212);
2899          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2900    
2901          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2902          !!!next-input-character;          !!!next-input-character;
2903    
2904          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2905          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2906    
2907          redo A;          redo A;
2908        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2909          !!!cp (213);          !!!cp (213);
2910          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2911    
2912          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2913          ## reconsume          ## reconsume
2914    
2915          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2916          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2917    
2918          redo A;          redo A;
2919        } else {        } else {
2920          !!!cp (214);          !!!cp (214);
2921          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2922              .= chr $self->{next_char};              .= chr $self->{nc};
2923            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2924                                  length $self->{ct}->{sysid});
2925    
2926          ## Stay in the state          ## Stay in the state
2927          !!!next-input-character;          !!!next-input-character;
2928          redo A;          redo A;
2929        }        }
2930      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2931        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2932          !!!cp (215);          !!!cp (215);
2933          ## Stay in the state          ## Stay in the state
2934          !!!next-input-character;          !!!next-input-character;
2935          redo A;          redo A;
2936        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2937          !!!cp (216);          !!!cp (216);
2938          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2939          !!!next-input-character;          !!!next-input-character;
2940    
2941          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2942    
2943          redo A;          redo A;
2944        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2945          !!!cp (217);          !!!cp (217);
2946          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
   
2947          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2948          ## reconsume          ## reconsume
2949    
2950          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2951          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2952    
2953          redo A;          redo A;
2954        } else {        } else {
2955          !!!cp (218);          !!!cp (218);
2956          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2957          #$self->{current_token}->{quirks} = 1;          #$self->{ct}->{quirks} = 1;
2958    
2959          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2960          !!!next-input-character;          !!!next-input-character;
2961          redo A;          redo A;
2962        }        }
2963      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2964        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2965          !!!cp (219);          !!!cp (219);
2966          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2967          !!!next-input-character;          !!!next-input-character;
2968    
2969          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2970    
2971          redo A;          redo A;
2972        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2973          !!!cp (220);          !!!cp (220);
2974          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2975          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2976          ## reconsume          ## reconsume
2977    
2978          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2979    
2980          redo A;          redo A;
2981        } else {        } else {
2982          !!!cp (221);          !!!cp (221);
2983            my $s = '';
2984            $self->{read_until}->($s, q[>], 0);
2985    
2986          ## Stay in the state          ## Stay in the state
2987          !!!next-input-character;          !!!next-input-character;
2988          redo A;          redo A;
2989        }        }
2990      } else {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
2991        die "$0: $self->{state}: Unknown state";        ## NOTE: "CDATA section state" in the state is jointly implemented
2992      }        ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
2993    } # A          ## and |CDATA_SECTION_MSE2_STATE|.
2994          
2995          if ($self->{nc} == 0x005D) { # ]
2996            !!!cp (221.1);
2997            $self->{state} = CDATA_SECTION_MSE1_STATE;
2998            !!!next-input-character;
2999            redo A;
3000          } elsif ($self->{nc} == -1) {
3001            $self->{state} = DATA_STATE;
3002            !!!next-input-character;
3003            if (length $self->{ct}->{data}) { # character
3004              !!!cp (221.2);
3005              !!!emit ($self->{ct}); # character
3006            } else {
3007              !!!cp (221.3);
3008              ## No token to emit. $self->{ct} is discarded.
3009            }        
3010            redo A;
3011          } else {
3012            !!!cp (221.4);
3013            $self->{ct}->{data} .= chr $self->{nc};
3014            $self->{read_until}->($self->{ct}->{data},
3015                                  q<]>,
3016                                  length $self->{ct}->{data});
3017    
3018    die "$0: _get_next_token: unexpected case";          ## Stay in the state.
3019  } # _get_next_token          !!!next-input-character;
3020            redo A;
3021          }
3022    
3023  sub _tokenize_attempt_to_consume_an_entity ($$$) {        ## ISSUE: "text tokens" in spec.
3024    my ($self, $in_attr, $additional) = @_;      } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3025          if ($self->{nc} == 0x005D) { # ]
3026            !!!cp (221.5);
3027            $self->{state} = CDATA_SECTION_MSE2_STATE;
3028            !!!next-input-character;
3029            redo A;
3030          } else {
3031            !!!cp (221.6);
3032            $self->{ct}->{data} .= ']';
3033            $self->{state} = CDATA_SECTION_STATE;
3034            ## Reconsume.
3035            redo A;
3036          }
3037        } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3038          if ($self->{nc} == 0x003E) { # >
3039            $self->{state} = DATA_STATE;
3040            !!!next-input-character;
3041            if (length $self->{ct}->{data}) { # character
3042              !!!cp (221.7);
3043              !!!emit ($self->{ct}); # character
3044            } else {
3045              !!!cp (221.8);
3046              ## No token to emit. $self->{ct} is discarded.
3047            }
3048            redo A;
3049          } elsif ($self->{nc} == 0x005D) { # ]
3050            !!!cp (221.9); # character
3051            $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3052            ## Stay in the state.
3053            !!!next-input-character;
3054            redo A;
3055          } else {
3056            !!!cp (221.11);
3057            $self->{ct}->{data} .= ']]'; # character
3058            $self->{state} = CDATA_SECTION_STATE;
3059            ## Reconsume.
3060            redo A;
3061          }
3062        } elsif ($self->{state} == ENTITY_STATE) {
3063          if ($is_space->{$self->{nc}} or
3064              {
3065                0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3066                $self->{entity_add} => 1,
3067              }->{$self->{nc}}) {
3068            !!!cp (1001);
3069            ## Don't consume
3070            ## No error
3071            ## Return nothing.
3072            #
3073          } elsif ($self->{nc} == 0x0023) { # #
3074            !!!cp (999);
3075            $self->{state} = ENTITY_HASH_STATE;
3076            $self->{s_kwd} = '#';
3077            !!!next-input-character;
3078            redo A;
3079          } elsif ((0x0041 <= $self->{nc} and
3080                    $self->{nc} <= 0x005A) or # A..Z
3081                   (0x0061 <= $self->{nc} and
3082                    $self->{nc} <= 0x007A)) { # a..z
3083            !!!cp (998);
3084            require Whatpm::_NamedEntityList;
3085            $self->{state} = ENTITY_NAME_STATE;
3086            $self->{s_kwd} = chr $self->{nc};
3087            $self->{entity__value} = $self->{s_kwd};
3088            $self->{entity__match} = 0;
3089            !!!next-input-character;
3090            redo A;
3091          } else {
3092            !!!cp (1027);
3093            !!!parse-error (type => 'bare ero');
3094            ## Return nothing.
3095            #
3096          }
3097    
3098    my ($l, $c) = ($self->{line_prev}, $self->{column_prev});        ## NOTE: No character is consumed by the "consume a character
3099          ## reference" algorithm.  In other word, there is an "&" character
3100          ## that does not introduce a character reference, which would be
3101          ## appended to the parent element or the attribute value in later
3102          ## process of the tokenizer.
3103    
3104          if ($self->{prev_state} == DATA_STATE) {
3105            !!!cp (997);
3106            $self->{state} = $self->{prev_state};
3107            ## Reconsume.
3108            !!!emit ({type => CHARACTER_TOKEN, data => '&',
3109                      line => $self->{line_prev},
3110                      column => $self->{column_prev},
3111                     });
3112            redo A;
3113          } else {
3114            !!!cp (996);
3115            $self->{ca}->{value} .= '&';
3116            $self->{state} = $self->{prev_state};
3117            ## Reconsume.
3118            redo A;
3119          }
3120        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3121          if ($self->{nc} == 0x0078 or # x
3122              $self->{nc} == 0x0058) { # X
3123            !!!cp (995);
3124            $self->{state} = HEXREF_X_STATE;
3125            $self->{s_kwd} .= chr $self->{nc};
3126            !!!next-input-character;
3127            redo A;
3128          } elsif (0x0030 <= $self->{nc} and
3129                   $self->{nc} <= 0x0039) { # 0..9
3130            !!!cp (994);
3131            $self->{state} = NCR_NUM_STATE;
3132            $self->{s_kwd} = $self->{nc} - 0x0030;
3133            !!!next-input-character;
3134            redo A;
3135          } else {
3136            !!!parse-error (type => 'bare nero',
3137                            line => $self->{line_prev},
3138                            column => $self->{column_prev} - 1);
3139    
3140    if ({          ## NOTE: According to the spec algorithm, nothing is returned,
3141         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,          ## and then "&#" is appended to the parent element or the attribute
3142         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR          ## value in the later processing.
3143         $additional => 1,  
3144        }->{$self->{next_char}}) {          if ($self->{prev_state} == DATA_STATE) {
3145      !!!cp (1001);            !!!cp (1019);
3146      ## Don't consume            $self->{state} = $self->{prev_state};
3147      ## No error            ## Reconsume.
3148      return undef;            !!!emit ({type => CHARACTER_TOKEN,
3149    } elsif ($self->{next_char} == 0x0023) { # #                      data => '&#',
3150      !!!next-input-character;                      line => $self->{line_prev},
3151      if ($self->{next_char} == 0x0078 or # x                      column => $self->{column_prev} - 1,
3152          $self->{next_char} == 0x0058) { # X                     });
3153        my $code;            redo A;
       X: {  
         my $x_char = $self->{next_char};  
         !!!next-input-character;  
         if (0x0030 <= $self->{next_char} and  
             $self->{next_char} <= 0x0039) { # 0..9  
           !!!cp (1002);  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_char} - 0x0030;  
           redo X;  
         } elsif (0x0061 <= $self->{next_char} and  
                  $self->{next_char} <= 0x0066) { # a..f  
           !!!cp (1003);  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_char} - 0x0060 + 9;  
           redo X;  
         } elsif (0x0041 <= $self->{next_char} and  
                  $self->{next_char} <= 0x0046) { # A..F  
           !!!cp (1004);  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_char} - 0x0040 + 9;  
           redo X;  
         } elsif (not defined $code) { # no hexadecimal digit  
           !!!cp (1005);  
           !!!parse-error (type => 'bare hcro', line => $l, column => $c);  
           !!!back-next-input-character ($x_char, $self->{next_char});  
           $self->{next_char} = 0x0023; # #  
           return undef;  
         } elsif ($self->{next_char} == 0x003B) { # ;  
           !!!cp (1006);  
           !!!next-input-character;  
3154          } else {          } else {
3155            !!!cp (1007);            !!!cp (993);
3156            !!!parse-error (type => 'no refc', line => $l, column => $c);            $self->{ca}->{value} .= '&#';
3157              $self->{state} = $self->{prev_state};
3158              ## Reconsume.
3159              redo A;
3160          }          }
3161          }
3162          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {      } elsif ($self->{state} == NCR_NUM_STATE) {
3163            !!!cp (1008);        if (0x0030 <= $self->{nc} and
3164            !!!parse-error (type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c);            $self->{nc} <= 0x0039) { # 0..9
           $code = 0xFFFD;  
         } elsif ($code > 0x10FFFF) {  
           !!!cp (1009);  
           !!!parse-error (type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c);  
           $code = 0xFFFD;  
         } elsif ($code == 0x000D) {  
           !!!cp (1010);  
           !!!parse-error (type => 'CR character reference', line => $l, column => $c);  
           $code = 0x000A;  
         } elsif (0x80 <= $code and $code <= 0x9F) {  
           !!!cp (1011);  
           !!!parse-error (type => (sprintf 'C1 character reference:U+%04X', $code), line => $l, column => $c);  
           $code = $c1_entity_char->{$code};  
         }  
   
         return {type => CHARACTER_TOKEN, data => chr $code,  
                 has_reference => 1, line => $l, column => $c};  
       } # X  
     } elsif (0x0030 <= $self->{next_char} and  
              $self->{next_char} <= 0x0039) { # 0..9  
       my $code = $self->{next_char} - 0x0030;  
       !!!next-input-character;  
         
       while (0x0030 <= $self->{next_char} and  
                 $self->{next_char} <= 0x0039) { # 0..9  
3165          !!!cp (1012);          !!!cp (1012);
3166          $code *= 10;          $self->{s_kwd} *= 10;
3167          $code += $self->{next_char} - 0x0030;          $self->{s_kwd} += $self->{nc} - 0x0030;
3168                    
3169            ## Stay in the state.
3170          !!!next-input-character;          !!!next-input-character;
3171        }          redo A;
3172          } elsif ($self->{nc} == 0x003B) { # ;
       if ($self->{next_char} == 0x003B) { # ;  
3173          !!!cp (1013);          !!!cp (1013);
3174          !!!next-input-character;          !!!next-input-character;
3175            #
3176        } else {        } else {
3177          !!!cp (1014);          !!!cp (1014);
3178          !!!parse-error (type => 'no refc', line => $l, column => $c);          !!!parse-error (type => 'no refc');
3179            ## Reconsume.
3180            #
3181        }        }
3182    
3183        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3184          my $l = $self->{line_prev};
3185          my $c = $self->{column_prev};
3186          if ($charref_map->{$code}) {
3187          !!!cp (1015);          !!!cp (1015);
3188          !!!parse-error (type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c);          !!!parse-error (type => 'invalid character reference',
3189          $code = 0xFFFD;                          text => (sprintf 'U+%04X', $code),
3190                            line => $l, column => $c);
3191            $code = $charref_map->{$code};
3192        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3193          !!!cp (1016);          !!!cp (1016);
3194          !!!parse-error (type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c);          !!!parse-error (type => 'invalid character reference',
3195                            text => (sprintf 'U-%08X', $code),
3196                            line => $l, column => $c);
3197          $code = 0xFFFD;          $code = 0xFFFD;
       } elsif ($code == 0x000D) {  
         !!!cp (1017);  
         !!!parse-error (type => 'CR character reference', line => $l, column => $c);  
         $code = 0x000A;  
       } elsif (0x80 <= $code and $code <= 0x9F) {  
         !!!cp (1018);  
         !!!parse-error (type => (sprintf 'C1 character reference:U+%04X', $code), line => $l, column => $c);  
         $code = $c1_entity_char->{$code};  
3198        }        }
3199          
3200        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1,        if ($self->{prev_state} == DATA_STATE) {
3201                line => $l, column => $c};          !!!cp (992);
3202      } else {          $self->{state} = $self->{prev_state};
3203        !!!cp (1019);          ## Reconsume.
3204        !!!parse-error (type => 'bare nero', line => $l, column => $c);          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3205        !!!back-next-input-character ($self->{next_char});                    line => $l, column => $c,
3206        $self->{next_char} = 0x0023; # #                   });
3207        return undef;          redo A;
3208      }        } else {
3209    } elsif ((0x0041 <= $self->{next_char} and          !!!cp (991);
3210              $self->{next_char} <= 0x005A) or          $self->{ca}->{value} .= chr $code;
3211             (0x0061 <= $self->{next_char} and          $self->{ca}->{has_reference} = 1;
3212              $self->{next_char} <= 0x007A)) {          $self->{state} = $self->{prev_state};
3213      my $entity_name = chr $self->{next_char};          ## Reconsume.
3214      !!!next-input-character;          redo A;
3215          }
3216      my $value = $entity_name;      } elsif ($self->{state} == HEXREF_X_STATE) {
3217      my $match = 0;        if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3218      require Whatpm::_NamedEntityList;            (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3219      our $EntityChar;            (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3220            # 0..9, A..F, a..f
3221      while (length $entity_name < 10 and          !!!cp (990);
3222             ## NOTE: Some number greater than the maximum length of entity name          $self->{state} = HEXREF_HEX_STATE;
3223             ((0x0041 <= $self->{next_char} and # a          $self->{s_kwd} = 0;
3224               $self->{next_char} <= 0x005A) or # x          ## Reconsume.
3225              (0x0061 <= $self->{next_char} and # a          redo A;
3226               $self->{next_char} <= 0x007A) or # z        } else {
3227              (0x0030 <= $self->{next_char} and # 0          !!!parse-error (type => 'bare hcro',
3228               $self->{next_char} <= 0x0039) or # 9                          line => $self->{line_prev},
3229              $self->{next_char} == 0x003B)) { # ;                          column => $self->{column_prev} - 2);
3230        $entity_name .= chr $self->{next_char};  
3231        if (defined $EntityChar->{$entity_name}) {          ## NOTE: According to the spec algorithm, nothing is returned,
3232          if ($self->{next_char} == 0x003B) { # ;          ## and then "&#" followed by "X" or "x" is appended to the parent
3233            !!!cp (1020);          ## element or the attribute value in the later processing.
3234            $value = $EntityChar->{$entity_name};  
3235            $match = 1;          if ($self->{prev_state} == DATA_STATE) {
3236            !!!next-input-character;            !!!cp (1005);
3237            last;            $self->{state} = $self->{prev_state};
3238              ## Reconsume.
3239              !!!emit ({type => CHARACTER_TOKEN,
3240                        data => '&' . $self->{s_kwd},
3241                        line => $self->{line_prev},
3242                        column => $self->{column_prev} - length $self->{s_kwd},
3243                       });
3244              redo A;
3245            } else {
3246              !!!cp (989);
3247              $self->{ca}->{value} .= '&' . $self->{s_kwd};
3248              $self->{state} = $self->{prev_state};
3249              ## Reconsume.
3250              redo A;
3251            }
3252          }
3253        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3254          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3255            # 0..9
3256            !!!cp (1002);
3257            $self->{s_kwd} *= 0x10;
3258            $self->{s_kwd} += $self->{nc} - 0x0030;
3259            ## Stay in the state.
3260            !!!next-input-character;
3261            redo A;
3262          } elsif (0x0061 <= $self->{nc} and
3263                   $self->{nc} <= 0x0066) { # a..f
3264            !!!cp (1003);
3265            $self->{s_kwd} *= 0x10;
3266            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3267            ## Stay in the state.
3268            !!!next-input-character;
3269            redo A;
3270          } elsif (0x0041 <= $self->{nc} and
3271                   $self->{nc} <= 0x0046) { # A..F
3272            !!!cp (1004);
3273            $self->{s_kwd} *= 0x10;
3274            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3275            ## Stay in the state.
3276            !!!next-input-character;
3277            redo A;
3278          } elsif ($self->{nc} == 0x003B) { # ;
3279            !!!cp (1006);
3280            !!!next-input-character;
3281            #
3282          } else {
3283            !!!cp (1007);
3284            !!!parse-error (type => 'no refc',
3285                            line => $self->{line},
3286                            column => $self->{column});
3287            ## Reconsume.
3288            #
3289          }
3290    
3291          my $code = $self->{s_kwd};
3292          my $l = $self->{line_prev};
3293          my $c = $self->{column_prev};
3294          if ($charref_map->{$code}) {
3295            !!!cp (1008);
3296            !!!parse-error (type => 'invalid character reference',
3297                            text => (sprintf 'U+%04X', $code),
3298                            line => $l, column => $c);
3299            $code = $charref_map->{$code};
3300          } elsif ($code > 0x10FFFF) {
3301            !!!cp (1009);
3302            !!!parse-error (type => 'invalid character reference',
3303                            text => (sprintf 'U-%08X', $code),
3304                            line => $l, column => $c);
3305            $code = 0xFFFD;
3306          }
3307    
3308          if ($self->{prev_state} == DATA_STATE) {
3309            !!!cp (988);
3310            $self->{state} = $self->{prev_state};
3311            ## Reconsume.
3312            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3313                      line => $l, column => $c,
3314                     });
3315            redo A;
3316          } else {
3317            !!!cp (987);
3318            $self->{ca}->{value} .= chr $code;
3319            $self->{ca}->{has_reference} = 1;
3320            $self->{state} = $self->{prev_state};
3321            ## Reconsume.
3322            redo A;
3323          }
3324        } elsif ($self->{state} == ENTITY_NAME_STATE) {
3325          if (length $self->{s_kwd} < 30 and
3326              ## NOTE: Some number greater than the maximum length of entity name
3327              ((0x0041 <= $self->{nc} and # a
3328                $self->{nc} <= 0x005A) or # x
3329               (0x0061 <= $self->{nc} and # a
3330                $self->{nc} <= 0x007A) or # z
3331               (0x0030 <= $self->{nc} and # 0
3332                $self->{nc} <= 0x0039) or # 9
3333               $self->{nc} == 0x003B)) { # ;
3334            our $EntityChar;
3335            $self->{s_kwd} .= chr $self->{nc};
3336            if (defined $EntityChar->{$self->{s_kwd}}) {
3337              if ($self->{nc} == 0x003B) { # ;
3338                !!!cp (1020);
3339                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3340                $self->{entity__match} = 1;
3341                !!!next-input-character;
3342                #
3343              } else {
3344                !!!cp (1021);
3345                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3346                $self->{entity__match} = -1;
3347                ## Stay in the state.
3348                !!!next-input-character;
3349                redo A;
3350              }
3351          } else {          } else {
3352            !!!cp (1021);            !!!cp (1022);
3353            $value = $EntityChar->{$entity_name};            $self->{entity__value} .= chr $self->{nc};
3354            $match = -1;            $self->{entity__match} *= 2;
3355              ## Stay in the state.
3356            !!!next-input-character;            !!!next-input-character;
3357              redo A;
3358            }
3359          }
3360    
3361          my $data;
3362          my $has_ref;
3363          if ($self->{entity__match} > 0) {
3364            !!!cp (1023);
3365            $data = $self->{entity__value};
3366            $has_ref = 1;
3367            #
3368          } elsif ($self->{entity__match} < 0) {
3369            !!!parse-error (type => 'no refc');
3370            if ($self->{prev_state} != DATA_STATE and # in attribute
3371                $self->{entity__match} < -1) {
3372              !!!cp (1024);
3373              $data = '&' . $self->{s_kwd};
3374              #
3375            } else {
3376              !!!cp (1025);
3377              $data = $self->{entity__value};
3378              $has_ref = 1;
3379              #
3380          }          }
3381        } else {        } else {
3382          !!!cp (1022);          !!!cp (1026);
3383          $value .= chr $self->{next_char};          !!!parse-error (type => 'bare ero',
3384          $match *= 2;                          line => $self->{line_prev},
3385          !!!next-input-character;                          column => $self->{column_prev} - length $self->{s_kwd});
3386            $data = '&' . $self->{s_kwd};
3387            #
3388        }        }
3389      }    
3390              ## NOTE: In these cases, when a character reference is found,
3391      if ($match > 0) {        ## it is consumed and a character token is returned, or, otherwise,
3392        !!!cp (1023);        ## nothing is consumed and returned, according to the spec algorithm.
3393        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,        ## In this implementation, anything that has been examined by the
3394                line => $l, column => $c};        ## tokenizer is appended to the parent element or the attribute value
3395      } elsif ($match < 0) {        ## as string, either literal string when no character reference or
3396        !!!parse-error (type => 'no refc', line => $l, column => $c);        ## entity-replaced string otherwise, in this stage, since any characters
3397        if ($in_attr and $match < -1) {        ## that would not be consumed are appended in the data state or in an
3398          !!!cp (1024);        ## appropriate attribute value state anyway.
3399          return {type => CHARACTER_TOKEN, data => '&'.$entity_name,  
3400                  line => $l, column => $c};        if ($self->{prev_state} == DATA_STATE) {
3401        } else {          !!!cp (986);
3402          !!!cp (1025);          $self->{state} = $self->{prev_state};
3403          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,          ## Reconsume.
3404                  line => $l, column => $c};          !!!emit ({type => CHARACTER_TOKEN,
3405                      data => $data,
3406                      line => $self->{line_prev},
3407                      column => $self->{column_prev} + 1 - length $self->{s_kwd},
3408                     });
3409            redo A;
3410          } else {
3411            !!!cp (985);
3412            $self->{ca}->{value} .= $data;
3413            $self->{ca}->{has_reference} = 1 if $has_ref;
3414            $self->{state} = $self->{prev_state};
3415            ## Reconsume.
3416            redo A;
3417        }        }
3418      } else {      } else {
3419        !!!cp (1026);        die "$0: $self->{state}: Unknown state";
       !!!parse-error (type => 'bare ero', line => $l, column => $c);  
       ## NOTE: "No characters are consumed" in the spec.  
       return {type => CHARACTER_TOKEN, data => '&'.$value,  
               line => $l, column => $c};  
3420      }      }
3421    } else {    } # A  
3422      !!!cp (1027);  
3423      ## no characters are consumed    die "$0: _get_next_token: unexpected case";
3424      !!!parse-error (type => 'bare ero', line => $l, column => $c);  } # _get_next_token
     return undef;  
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3425    
3426  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3427    my $self = shift;    my $self = shift;
# Line 2452  sub _initialize_tree_constructor ($) { Line 3430  sub _initialize_tree_constructor ($) {
3430    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3431    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3432    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3433      $self->{document}->set_user_data (manakai_source_line => 1);
3434      $self->{document}->set_user_data (manakai_source_column => 1);
3435  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3436    
3437  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 2506  sub _tree_construction_initial ($) { Line 3486  sub _tree_construction_initial ($) {
3486        ## language.        ## language.
3487        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
3488        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3489        $doctype_name =~ tr/a-z/A-Z/;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3490        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3491            defined $token->{public_identifier} or            defined $token->{sysid}) {
           defined $token->{system_identifier}) {  
3492          !!!cp ('t1');          !!!cp ('t1');
3493          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
3494        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3495          !!!cp ('t2');          !!!cp ('t2');
         ## ISSUE: ASCII case-insensitive? (in fact it does not matter)  
3496          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
3497          } elsif (defined $token->{pubid}) {
3498            if ($token->{pubid} eq 'XSLT-compat') {
3499              !!!cp ('t1.2');
3500              !!!parse-error (type => 'XSLT-compat', token => $token,
3501                              level => $self->{level}->{should});
3502            } else {
3503              !!!parse-error (type => 'not HTML5', token => $token);
3504            }
3505        } else {        } else {
3506          !!!cp ('t3');          !!!cp ('t3');
3507            #
3508        }        }
3509                
3510        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
3511          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3512        $doctype->public_id ($token->{public_identifier})        ## NOTE: Default value for both |public_id| and |system_id| attributes
3513            if defined $token->{public_identifier};        ## are empty strings, so that we don't set any value in missing cases.
3514        $doctype->system_id ($token->{system_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3515            if defined $token->{system_identifier};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
3516        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3517        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3518        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
# Line 2533  sub _tree_construction_initial ($) { Line 3520  sub _tree_construction_initial ($) {
3520        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3521          !!!cp ('t4');          !!!cp ('t4');
3522          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3523        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3524          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3525          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3526          if ({          my $prefix = [
3527            "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3528            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3529            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3530            "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 1//",
3531            "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 2//",
3532            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3533            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3534            "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT//",
3535            "-//IETF//DTD HTML 2.0//EN" => 1,            "-//IETF//DTD HTML 2.0//",
3536            "-//IETF//DTD HTML 2.1E//EN" => 1,            "-//IETF//DTD HTML 2.1E//",
3537            "-//IETF//DTD HTML 3.0//EN" => 1,            "-//IETF//DTD HTML 3.0//",
3538            "-//IETF//DTD HTML 3.0//EN//" => 1,            "-//IETF//DTD HTML 3.2 FINAL//",
3539            "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,            "-//IETF//DTD HTML 3.2//",
3540            "-//IETF//DTD HTML 3.2//EN" => 1,            "-//IETF//DTD HTML 3//",
3541            "-//IETF//DTD HTML 3//EN" => 1,            "-//IETF//DTD HTML LEVEL 0//",
3542            "-//IETF//DTD HTML LEVEL 0//EN" => 1,            "-//IETF//DTD HTML LEVEL 1//",
3543            "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,            "-//IETF//DTD HTML LEVEL 2//",
3544            "-//IETF//DTD HTML LEVEL 1//EN" => 1,            "-//IETF//DTD HTML LEVEL 3//",
3545            "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 0//",
3546            "-//IETF//DTD HTML LEVEL 2//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 1//",
3547            "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 2//",
3548            "-//IETF//DTD HTML LEVEL 3//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 3//",
3549            "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,            "-//IETF//DTD HTML STRICT//",
3550            "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,            "-//IETF//DTD HTML//",
3551            "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,            "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3552            "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3553            "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3554            "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3555            "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3556            "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3557            "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3558            "-//IETF//DTD HTML STRICT//EN" => 1,            "-//NETSCAPE COMM. CORP.//DTD HTML//",
3559            "-//IETF//DTD HTML STRICT//EN//2.0" => 1,            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3560            "-//IETF//DTD HTML STRICT//EN//3.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3561            "-//IETF//DTD HTML//EN" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3562            "-//IETF//DTD HTML//EN//2.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3563            "-//IETF//DTD HTML//EN//3.0" => 1,            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3564            "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3565            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3566            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3567            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3568            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3569            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,            "-//W3C//DTD HTML 3 1995-03-24//",
3570            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,            "-//W3C//DTD HTML 3.2 DRAFT//",
3571            "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,            "-//W3C//DTD HTML 3.2 FINAL//",
3572            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,            "-//W3C//DTD HTML 3.2//",
3573            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,            "-//W3C//DTD HTML 3.2S DRAFT//",
3574            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 FRAMESET//",
3575            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3576            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3577            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3578            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,            "-//W3C//DTD W3 HTML//",
3579            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,            "-//W3O//DTD W3 HTML 3.0//",
3580            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3581            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML//",
3582            "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,          ]; # $prefix
3583            "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,          my $match;
3584            "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,          for (@$prefix) {
3585            "-//W3C//DTD HTML 3.2//EN" => 1,            if (substr ($prefix, 0, length $_) eq $_) {
3586            "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,              $match = 1;
3587            "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,              last;
3588            "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,            }
3589            "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,          }
3590            "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,          if ($match or
3591            "-//W3C//DTD W3 HTML//EN" => 1,              $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3592            "-//W3O//DTD W3 HTML 3.0//EN" => 1,              $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3593            "-//W3O//DTD W3 HTML 3.0//EN//" => 1,              $pubid eq "HTML") {
           "-//W3O//DTD W3 HTML STRICT 3.0//EN//" => 1,  
           "-//WEBTECHS//DTD MOZILLA HTML 2.0//EN" => 1,  
           "-//WEBTECHS//DTD MOZILLA HTML//EN" => 1,  
           "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" => 1,  
           "HTML" => 1,  
         }->{$pubid}) {  
3594            !!!cp ('t5');            !!!cp ('t5');
3595            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3596          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3597                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3598            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3599              !!!cp ('t6');              !!!cp ('t6');
3600              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3601            } else {            } else {
3602              !!!cp ('t7');              !!!cp ('t7');
3603              $self->{document}->manakai_compat_mode ('limited quirks');              $self->{document}->manakai_compat_mode ('limited quirks');
3604            }            }
3605          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3606                   $pubid eq "-//W3C//DTD XHTML 1.0 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3607            !!!cp ('t8');            !!!cp ('t8');
3608            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3609          } else {          } else {
# Line 2631  sub _tree_construction_initial ($) { Line 3612  sub _tree_construction_initial ($) {
3612        } else {        } else {
3613          !!!cp ('t10');          !!!cp ('t10');
3614        }        }
3615        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3616          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3617          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3618          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
3619            ## TODO: Check the spec: PUBLIC "(limited quirks)" "(quirks)"            ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3620              ## marked as quirks.
3621            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3622            !!!cp ('t11');            !!!cp ('t11');
3623          } else {          } else {
# Line 2658  sub _tree_construction_initial ($) { Line 3640  sub _tree_construction_initial ($) {
3640        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3641        ## Go to the "before html" insertion mode.        ## Go to the "before html" insertion mode.
3642        ## reprocess        ## reprocess
3643          !!!ack-later;
3644        return;        return;
3645      } elsif ($token->{type} == CHARACTER_TOKEN) {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3646        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3647          ## Ignore the token          ## Ignore the token
3648    
3649          unless (length $token->{data}) {          unless (length $token->{data}) {
# Line 2717  sub _tree_construction_root_element ($) Line 3700  sub _tree_construction_root_element ($)
3700          !!!next-token;          !!!next-token;
3701          redo B;          redo B;
3702        } elsif ($token->{type} == CHARACTER_TOKEN) {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3703          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3704            ## Ignore the token.            ## Ignore the token.
3705    
3706            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 2738  sub _tree_construction_root_element ($) Line 3721  sub _tree_construction_root_element ($)
3721        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3722          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
3723            my $root_element;            my $root_element;
3724            !!!create-element ($root_element, $token->{tag_name}, $token->{attributes});            !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3725            $self->{document}->append_child ($root_element);            $self->{document}->append_child ($root_element);
3726            push @{$self->{open_elements}}, [$root_element, 'html'];            push @{$self->{open_elements}},
3727                  [$root_element, $el_category->{html}];
3728    
3729            if ($token->{attributes}->{manifest}) {            if ($token->{attributes}->{manifest}) {
3730              !!!cp ('t24');              !!!cp ('t24');
3731              $self->{application_cache_selection}              $self->{application_cache_selection}
3732                  ->($token->{attributes}->{manifest}->{value});                  ->($token->{attributes}->{manifest}->{value});
3733              ## ISSUE: No relative reference resolution?              ## ISSUE: Spec is unclear on relative references.
3734                ## According to Hixie (#whatwg 2008-03-19), it should be
3735                ## resolved against the base URI of the document in HTML
3736                ## or xml:base of the element in XHTML.
3737            } else {            } else {
3738              !!!cp ('t25');              !!!cp ('t25');
3739              $self->{application_cache_selection}->(undef);              $self->{application_cache_selection}->(undef);
3740            }            }
3741    
3742              !!!nack ('t25c');
3743    
3744            !!!next-token;            !!!next-token;
3745            return; ## Go to the "before head" insertion mode.            return; ## Go to the "before head" insertion mode.
3746          } else {          } else {
# Line 2768  sub _tree_construction_root_element ($) Line 3757  sub _tree_construction_root_element ($)
3757          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3758        }        }
3759    
3760      my $root_element; !!!create-element ($root_element, 'html');      my $root_element;
3761        !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3762      $self->{document}->append_child ($root_element);      $self->{document}->append_child ($root_element);
3763      push @{$self->{open_elements}}, [$root_element, 'html'];      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3764    
3765      $self->{application_cache_selection}->(undef);      $self->{application_cache_selection}->(undef);
3766    
3767      ## NOTE: Reprocess the token.      ## NOTE: Reprocess the token.
3768        !!!ack-later;
3769      return; ## Go to the "before head" insertion mode.      return; ## Go to the "before head" insertion mode.
3770    
3771      ## ISSUE: There is an issue in the spec      ## ISSUE: There is an issue in the spec
# Line 2798  sub _reset_insertion_mode ($) { Line 3789  sub _reset_insertion_mode ($) {
3789        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3790          $last = 1;          $last = 1;
3791          if (defined $self->{inner_html_node}) {          if (defined $self->{inner_html_node}) {
3792            if ($self->{inner_html_node}->[1] eq 'td' or            !!!cp ('t28');
3793                $self->{inner_html_node}->[1] eq 'th') {            $node = $self->{inner_html_node};
3794              !!!cp ('t27');          } else {
3795              #            die "_reset_insertion_mode: t27";
           } else {  
             !!!cp ('t28');  
             $node = $self->{inner_html_node};  
           }  
3796          }          }
3797        }        }
3798              
3799        ## Step 4..13        ## Step 4..14
3800        my $new_mode = {        my $new_mode;
3801          if ($node->[1] & FOREIGN_EL) {
3802            !!!cp ('t28.1');
3803            ## NOTE: Strictly spaking, the line below only applies to MathML and
3804            ## SVG elements.  Currently the HTML syntax supports only MathML and
3805            ## SVG elements as foreigners.
3806            $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3807          } elsif ($node->[1] & TABLE_CELL_EL) {
3808            if ($last) {
3809              !!!cp ('t28.2');
3810              #
3811            } else {
3812              !!!cp ('t28.3');
3813              $new_mode = IN_CELL_IM;
3814            }
3815          } else {
3816            !!!cp ('t28.4');
3817            $new_mode = {
3818                        select => IN_SELECT_IM,                        select => IN_SELECT_IM,
3819                        ## NOTE: |option| and |optgroup| do not set                        ## NOTE: |option| and |optgroup| do not set
3820                        ## insertion mode to "in select" by themselves.                        ## insertion mode to "in select" by themselves.
                       td => IN_CELL_IM,  
                       th => IN_CELL_IM,  
3821                        tr => IN_ROW_IM,                        tr => IN_ROW_IM,
3822                        tbody => IN_TABLE_BODY_IM,                        tbody => IN_TABLE_BODY_IM,
3823                        thead => IN_TABLE_BODY_IM,                        thead => IN_TABLE_BODY_IM,
# Line 2826  sub _reset_insertion_mode ($) { Line 3828  sub _reset_insertion_mode ($) {
3828                        head => IN_BODY_IM, # not in head!                        head => IN_BODY_IM, # not in head!
3829                        body => IN_BODY_IM,                        body => IN_BODY_IM,
3830                        frameset => IN_FRAMESET_IM,                        frameset => IN_FRAMESET_IM,
3831                       }->{$node->[1]};                       }->{$node->[0]->manakai_local_name};
3832          }
3833        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3834                
3835        ## Step 14        ## Step 15
3836        if ($node->[1] eq 'html') {        if ($node->[1] & HTML_EL) {
3837          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3838            !!!cp ('t29');            !!!cp ('t29');
3839            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
# Line 2844  sub _reset_insertion_mode ($) { Line 3847  sub _reset_insertion_mode ($) {
3847          !!!cp ('t31');          !!!cp ('t31');
3848        }        }
3849                
3850        ## Step 15        ## Step 16
3851        $self->{insertion_mode} = IN_BODY_IM and return if $last;        $self->{insertion_mode} = IN_BODY_IM and return if $last;
3852                
3853        ## Step 16        ## Step 17
3854        $i--;        $i--;
3855        $node = $self->{open_elements}->[$i];        $node = $self->{open_elements}->[$i];
3856                
3857        ## Step 17        ## Step 18
3858        redo S3;        redo S3;
3859      } # S3      } # S3
3860    
# Line 2963  sub _tree_construction_main ($) { Line 3966  sub _tree_construction_main ($) {
3966      ## Step 1      ## Step 1
3967      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
3968      my $el;      my $el;
3969      !!!create-element ($el, $start_tag_name, $token->{attributes});      !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);
3970    
3971      ## Step 2      ## Step 2
3972      $insert->($el);      $insert->($el);
# Line 2974  sub _tree_construction_main ($) { Line 3977  sub _tree_construction_main ($) {
3977    
3978      ## Step 4      ## Step 4
3979      my $text = '';      my $text = '';
3980        !!!nack ('t40.1');
3981      !!!next-token;      !!!next-token;
3982      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
3983        !!!cp ('t40');        !!!cp ('t40');
# Line 3000  sub _tree_construction_main ($) { Line 4004  sub _tree_construction_main ($) {
4004        ## NOTE: An end-of-file token.        ## NOTE: An end-of-file token.
4005        if ($content_model_flag == CDATA_CONTENT_MODEL) {        if ($content_model_flag == CDATA_CONTENT_MODEL) {
4006          !!!cp ('t43');          !!!cp ('t43');
4007          !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token);          !!!parse-error (type => 'in CDATA:#eof', token => $token);
4008        } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {        } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
4009          !!!cp ('t44');          !!!cp ('t44');
4010          !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token);          !!!parse-error (type => 'in RCDATA:#eof', token => $token);
4011        } else {        } else {
4012          die "$0: $content_model_flag in parse_rcdata";          die "$0: $content_model_flag in parse_rcdata";
4013        }        }
# Line 3013  sub _tree_construction_main ($) { Line 4017  sub _tree_construction_main ($) {
4017    
4018    my $script_start_tag = sub () {    my $script_start_tag = sub () {
4019      my $script_el;      my $script_el;
4020      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
4021      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
4022    
4023      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
4024      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
4025            
4026      my $text = '';      my $text = '';
4027        !!!nack ('t45.1');
4028      !!!next-token;      !!!next-token;
4029      while ($token->{type} == CHARACTER_TOKEN) {      while ($token->{type} == CHARACTER_TOKEN) {
4030        !!!cp ('t45');        !!!cp ('t45');
# Line 3039  sub _tree_construction_main ($) { Line 4044  sub _tree_construction_main ($) {
4044        ## Ignore the token        ## Ignore the token
4045      } else {      } else {
4046        !!!cp ('t48');        !!!cp ('t48');
4047        !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token);        !!!parse-error (type => 'in CDATA:#eof', token => $token);
4048        ## ISSUE: And ignore?        ## ISSUE: And ignore?
4049        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4050      }      }
# Line 3077  sub _tree_construction_main ($) { Line 4082  sub _tree_construction_main ($) {
4082        my $formatting_element;        my $formatting_element;
4083        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4084        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4085          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4086              !!!cp ('t52');
4087              last AFE;
4088            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4089                         eq $tag_name) {
4090            !!!cp ('t51');            !!!cp ('t51');
4091            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
4092            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
4093            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           !!!cp ('t52');  
           last AFE;  
4094          }          }
4095        } # AFE        } # AFE
4096        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4097          !!!cp ('t53');          !!!cp ('t53');
4098          !!!parse-error (type => 'unmatched end tag:'.$tag_name, token => $end_tag_token);          !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4099          ## Ignore the token          ## Ignore the token
4100          !!!next-token;          !!!next-token;
4101          return;          return;
# Line 3106  sub _tree_construction_main ($) { Line 4112  sub _tree_construction_main ($) {
4112              last INSCOPE;              last INSCOPE;
4113            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4114              !!!cp ('t55');              !!!cp ('t55');
4115              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},              !!!parse-error (type => 'unmatched end tag',
4116                                text => $token->{tag_name},
4117                              token => $end_tag_token);                              token => $end_tag_token);
4118              ## Ignore the token              ## Ignore the token
4119              !!!next-token;              !!!next-token;
4120              return;              return;
4121            }            }
4122          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
                   applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
4123            !!!cp ('t56');            !!!cp ('t56');
4124            $in_scope = 0;            $in_scope = 0;
4125          }          }
4126        } # INSCOPE        } # INSCOPE
4127        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4128          !!!cp ('t57');          !!!cp ('t57');
4129          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},          !!!parse-error (type => 'unmatched end tag',
4130                            text => $token->{tag_name},
4131                          token => $end_tag_token);                          token => $end_tag_token);
4132          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4133          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
# Line 3130  sub _tree_construction_main ($) { Line 4135  sub _tree_construction_main ($) {
4135        }        }
4136        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4137          !!!cp ('t58');          !!!cp ('t58');
4138          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1],          !!!parse-error (type => 'not closed',
4139                            text => $self->{open_elements}->[-1]->[0]
4140                                ->manakai_local_name,
4141                          token => $end_tag_token);                          token => $end_tag_token);
4142        }        }
4143                
# Line 3139  sub _tree_construction_main ($) { Line 4146  sub _tree_construction_main ($) {
4146        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4147        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4148          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4149          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4150              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4151              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4152               $scoping_category->{$node->[1]})) { ## Scoping is redundant, maybe               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4153            !!!cp ('t59');            !!!cp ('t59');
4154            $furthest_block = $node;            $furthest_block = $node;
4155            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
# Line 3228  sub _tree_construction_main ($) { Line 4235  sub _tree_construction_main ($) {
4235        } # S7          } # S7  
4236                
4237        ## Step 8        ## Step 8
4238        if ({        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
            table => 1, tbody => 1, tfoot => 1, thead => 1, tr => 1,  
           }->{$common_ancestor_node->[1]}) {  
4239          my $foster_parent_element;          my $foster_parent_element;
4240          my $next_sibling;          my $next_sibling;
4241                           OE: for (reverse 0..$#{$self->{open_elements}}) {          OE: for (reverse 0..$#{$self->{open_elements}}) {
4242                             if ($self->{open_elements}->[$_]->[1] eq 'table') {            if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4243                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4244                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4245                                 !!!cp ('t65.1');                                 !!!cp ('t65.1');
# Line 3307  sub _tree_construction_main ($) { Line 4312  sub _tree_construction_main ($) {
4312    
4313    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4314      my $child = shift;      my $child = shift;
4315      if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
          table => 1, tbody => 1, tfoot => 1, thead => 1, tr => 1,  
         }->{$self->{open_elements}->[-1]->[1]}) {  
4316        # MUST        # MUST
4317        my $foster_parent_element;        my $foster_parent_element;
4318        my $next_sibling;        my $next_sibling;
4319                           OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4320                             if ($self->{open_elements}->[$_]->[1] eq 'table') {          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4321                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4322                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4323                                 !!!cp ('t70');                                 !!!cp ('t70');
# Line 3339  sub _tree_construction_main ($) { Line 4342  sub _tree_construction_main ($) {
4342      }      }
4343    }; # $insert_to_foster    }; # $insert_to_foster
4344    
4345    B: {    B: while (1) {
4346      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
4347        !!!cp ('t73');        !!!cp ('t73');
4348        !!!parse-error (type => 'DOCTYPE in the middle', token => $token);        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4349        ## Ignore the token        ## Ignore the token
4350        ## Stay in the phase        ## Stay in the phase
4351        !!!next-token;        !!!next-token;
4352        redo B;        next B;
4353      } elsif ($token->{type} == START_TAG_TOKEN and      } elsif ($token->{type} == START_TAG_TOKEN and
4354               $token->{tag_name} eq 'html') {               $token->{tag_name} eq 'html') {
4355        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4356          !!!cp ('t79');          !!!cp ('t79');
4357          !!!parse-error (type => 'after html:html', token => $token);          !!!parse-error (type => 'after html', text => 'html', token => $token);
4358          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
4359        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4360          !!!cp ('t80');          !!!cp ('t80');
4361          !!!parse-error (type => 'after html:html', token => $token);          !!!parse-error (type => 'after html', text => 'html', token => $token);
4362          $self->{insertion_mode} = AFTER_FRAMESET_IM;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4363        } else {        } else {
4364          !!!cp ('t81');          !!!cp ('t81');
# Line 3372  sub _tree_construction_main ($) { Line 4375  sub _tree_construction_main ($) {
4375               $token->{attributes}->{$attr_name}->{value});               $token->{attributes}->{$attr_name}->{value});
4376          }          }
4377        }        }
4378          !!!nack ('t84.1');
4379        !!!next-token;        !!!next-token;
4380        redo B;        next B;
4381      } elsif ($token->{type} == COMMENT_TOKEN) {      } elsif ($token->{type} == COMMENT_TOKEN) {
4382        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
4383        if ($self->{insertion_mode} & AFTER_HTML_IMS) {        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
# Line 3387  sub _tree_construction_main ($) { Line 4391  sub _tree_construction_main ($) {
4391          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
4392        }        }
4393        !!!next-token;        !!!next-token;
4394        redo B;        next B;
4395      } elsif ($self->{insertion_mode} & HEAD_IMS) {      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4396        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4397          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          !!!cp ('t87.1');
4398            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4399            !!!next-token;
4400            next B;
4401          } elsif ($token->{type} == START_TAG_TOKEN) {
4402            if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4403                 $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4404                not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4405                ($token->{tag_name} eq 'svg' and
4406                 $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
4407              ## NOTE: "using the rules for secondary insertion mode"then"continue"
4408              !!!cp ('t87.2');
4409              #
4410            } elsif ({
4411                      b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4412                      center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4413                      em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4414                      h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4415                      img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4416                      nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4417                      small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4418                      sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4419                     }->{$token->{tag_name}}) {
4420              !!!cp ('t87.2');
4421              !!!parse-error (type => 'not closed',
4422                              text => $self->{open_elements}->[-1]->[0]
4423                                  ->manakai_local_name,
4424                              token => $token);
4425    
4426              pop @{$self->{open_elements}}
4427                  while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4428    
4429              $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4430              ## Reprocess.
4431              next B;
4432            } else {
4433              my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4434              my $tag_name = $token->{tag_name};
4435              if ($nsuri eq $SVG_NS) {
4436                $tag_name = {
4437                   altglyph => 'altGlyph',
4438                   altglyphdef => 'altGlyphDef',
4439                   altglyphitem => 'altGlyphItem',
4440                   animatecolor => 'animateColor',
4441                   animatemotion => 'animateMotion',
4442                   animatetransform => 'animateTransform',
4443                   clippath => 'clipPath',
4444                   feblend => 'feBlend',
4445                   fecolormatrix => 'feColorMatrix',
4446                   fecomponenttransfer => 'feComponentTransfer',
4447                   fecomposite => 'feComposite',
4448                   feconvolvematrix => 'feConvolveMatrix',
4449                   fediffuselighting => 'feDiffuseLighting',
4450                   fedisplacementmap => 'feDisplacementMap',
4451                   fedistantlight => 'feDistantLight',
4452                   feflood => 'feFlood',
4453                   fefunca => 'feFuncA',
4454                   fefuncb => 'feFuncB',
4455                   fefuncg => 'feFuncG',
4456                   fefuncr => 'feFuncR',
4457                   fegaussianblur => 'feGaussianBlur',
4458                   feimage => 'feImage',
4459                   femerge => 'feMerge',
4460                   femergenode => 'feMergeNode',
4461                   femorphology => 'feMorphology',
4462                   feoffset => 'feOffset',
4463                   fepointlight => 'fePointLight',
4464                   fespecularlighting => 'feSpecularLighting',
4465                   fespotlight => 'feSpotLight',
4466                   fetile => 'feTile',
4467                   feturbulence => 'feTurbulence',
4468                   foreignobject => 'foreignObject',
4469                   glyphref => 'glyphRef',
4470                   lineargradient => 'linearGradient',
4471                   radialgradient => 'radialGradient',
4472                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4473                   textpath => 'textPath',  
4474                }->{$tag_name} || $tag_name;
4475              }
4476    
4477              ## "adjust SVG attributes" (SVG only) - done in insert-element-f
4478    
4479              ## "adjust foreign attributes" - done in insert-element-f
4480    
4481              !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
4482    
4483              if ($self->{self_closing}) {
4484                pop @{$self->{open_elements}};
4485                !!!ack ('t87.3');
4486              } else {
4487                !!!cp ('t87.4');
4488              }
4489    
4490              !!!next-token;
4491              next B;
4492            }
4493          } elsif ($token->{type} == END_TAG_TOKEN) {
4494            ## NOTE: "using the rules for secondary insertion mode" then "continue"
4495            !!!cp ('t87.5');
4496            #
4497          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4498            !!!cp ('t87.6');
4499            !!!parse-error (type => 'not closed',
4500                            text => $self->{open_elements}->[-1]->[0]
4501                                ->manakai_local_name,
4502                            token => $token);
4503    
4504            pop @{$self->{open_elements}}
4505                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4506    
4507            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4508            ## Reprocess.
4509            next B;
4510          } else {
4511            die "$0: $token->{type}: Unknown token type";        
4512          }
4513        }
4514    
4515        if ($self->{insertion_mode} & HEAD_IMS) {
4516          if ($token->{type} == CHARACTER_TOKEN) {
4517            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4518            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4519              !!!cp ('t88.2');              !!!cp ('t88.2');
4520              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4521                #
4522            } else {            } else {
4523              !!!cp ('t88.1');              !!!cp ('t88.1');
4524              ## Ignore the token.              ## Ignore the token.
4525              !!!next-token;              #
             redo B;  
4526            }            }
4527            unless (length $token->{data}) {            unless (length $token->{data}) {
4528              !!!cp ('t88');              !!!cp ('t88');
4529              !!!next-token;              !!!next-token;
4530              redo B;              next B;
4531            }            }
4532    ## TODO: set $token->{column} appropriately
4533          }          }
4534    
4535          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4536            !!!cp ('t89');            !!!cp ('t89');
4537            ## As if <head>            ## As if <head>
4538            !!!create-element ($self->{head_element}, 'head');            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4539            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4540            push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            push @{$self->{open_elements}},
4541                  [$self->{head_element}, $el_category->{head}];
4542    
4543            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4544            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
# Line 3422  sub _tree_construction_main ($) { Line 4548  sub _tree_construction_main ($) {
4548            !!!cp ('t90');            !!!cp ('t90');
4549            ## As if </noscript>            ## As if </noscript>
4550            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4551            !!!parse-error (type => 'in noscript:#character', token => $token);            !!!parse-error (type => 'in noscript:#text', token => $token);
4552                        
4553            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4554            ## As if </head>            ## As if </head>
# Line 3438  sub _tree_construction_main ($) { Line 4564  sub _tree_construction_main ($) {
4564            !!!cp ('t92');            !!!cp ('t92');
4565          }          }
4566    
4567              ## "after head" insertion mode          ## "after head" insertion mode
4568              ## As if <body>          ## As if <body>
4569              !!!insert-element ('body');          !!!insert-element ('body',, $token);
4570              $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
4571              ## reprocess          ## reprocess
4572              redo B;          next B;
4573            } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
4574              if ($token->{tag_name} eq 'head') {          if ($token->{tag_name} eq 'head') {
4575                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4576                  !!!cp ('t93');              !!!cp ('t93');
4577                  !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes});              !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4578                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              $self->{open_elements}->[-1]->[0]->append_child
4579                  push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}];                  ($self->{head_element});
4580                  $self->{insertion_mode} = IN_HEAD_IM;              push @{$self->{open_elements}},
4581                  !!!next-token;                  [$self->{head_element}, $el_category->{head}];
4582                  redo B;              $self->{insertion_mode} = IN_HEAD_IM;
4583                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              !!!nack ('t93.1');
4584                  !!!cp ('t94');              !!!next-token;
4585                  #              next B;
4586                } else {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4587                  !!!cp ('t95');              !!!cp ('t93.2');
4588                  !!!parse-error (type => 'in head:head', token => $token); # or in head noscript              !!!parse-error (type => 'after head', text => 'head',
4589                  ## Ignore the token                              token => $token);
4590                  !!!next-token;              ## Ignore the token
4591                  redo B;              !!!nack ('t93.3');
4592                }              !!!next-token;
4593              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              next B;
4594                !!!cp ('t96');            } else {
4595                ## As if <head>              !!!cp ('t95');
4596                !!!create-element ($self->{head_element}, 'head');              !!!parse-error (type => 'in head:head',
4597                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                              token => $token); # or in head noscript
4598                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              ## Ignore the token
4599                !!!nack ('t95.1');
4600                !!!next-token;
4601                next B;
4602              }
4603            } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4604              !!!cp ('t96');
4605              ## As if <head>
4606              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4607              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4608              push @{$self->{open_elements}},
4609                  [$self->{head_element}, $el_category->{head}];
4610    
4611                $self->{insertion_mode} = IN_HEAD_IM;            $self->{insertion_mode} = IN_HEAD_IM;
4612                ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4613              } else {          } else {
4614                !!!cp ('t97');            !!!cp ('t97');
4615              }          }
4616    
4617              if ($token->{tag_name} eq 'base') {              if ($token->{tag_name} eq 'base') {
4618                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4619                  !!!cp ('t98');                  !!!cp ('t98');
4620                  ## As if </noscript>                  ## As if </noscript>
4621                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4622                  !!!parse-error (type => 'in noscript:base', token => $token);                  !!!parse-error (type => 'in noscript', text => 'base',
4623                                    token => $token);
4624                                
4625                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4626                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
# Line 3493  sub _tree_construction_main ($) { Line 4631  sub _tree_construction_main ($) {
4631                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4632                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4633                  !!!cp ('t100');                  !!!cp ('t100');
4634                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4635                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4636                    push @{$self->{open_elements}},
4637                        [$self->{head_element}, $el_category->{head}];
4638                } else {                } else {
4639                  !!!cp ('t101');                  !!!cp ('t101');
4640                }                }
4641                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4642                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
4643                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4644                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4645                  !!!nack ('t101.1');
4646                !!!next-token;                !!!next-token;
4647                redo B;                next B;
4648              } elsif ($token->{tag_name} eq 'link') {              } elsif ($token->{tag_name} eq 'link') {
4649                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4650                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4651                  !!!cp ('t102');                  !!!cp ('t102');
4652                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4653                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4654                    push @{$self->{open_elements}},
4655                        [$self->{head_element}, $el_category->{head}];
4656                } else {                } else {
4657                  !!!cp ('t103');                  !!!cp ('t103');
4658                }                }
4659                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4660                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
4661                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4662                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4663                  !!!ack ('t103.1');
4664                !!!next-token;                !!!next-token;
4665                redo B;                next B;
4666              } elsif ($token->{tag_name} eq 'meta') {              } elsif ($token->{tag_name} eq 'meta') {
4667                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4668                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4669                  !!!cp ('t104');                  !!!cp ('t104');
4670                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4671                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4672                    push @{$self->{open_elements}},
4673                        [$self->{head_element}, $el_category->{head}];
4674                } else {                } else {
4675                  !!!cp ('t105');                  !!!cp ('t105');
4676                }                }
4677                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4678                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
4679    
4680                unless ($self->{confident}) {                unless ($self->{confident}) {
4681                  if ($token->{attributes}->{charset}) { ## TODO: And if supported                  if ($token->{attributes}->{charset}) {
4682                    !!!cp ('t106');                    !!!cp ('t106');
4683                      ## NOTE: Whether the encoding is supported or not is handled
4684                      ## in the {change_encoding} callback.
4685                    $self->{change_encoding}                    $self->{change_encoding}
4686                        ->($self, $token->{attributes}->{charset}->{value});                        ->($self, $token->{attributes}->{charset}->{value},
4687                             $token);
4688                                        
4689                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4690                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
4691                                             $token->{attributes}->{charset}                                             $token->{attributes}->{charset}
4692                                                 ->{has_reference});                                                 ->{has_reference});
4693                  } elsif ($token->{attributes}->{content}) {                  } elsif ($token->{attributes}->{content}) {
                   ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
4694                    if ($token->{attributes}->{content}->{value}                    if ($token->{attributes}->{content}->{value}
4695                        =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4696                            [\x09-\x0D\x20]*=                            [\x09\x0A\x0C\x0D\x20]*=
4697                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4698                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                            ([^"'\x09\x0A\x0C\x0D\x20]
4699                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4700                      !!!cp ('t107');                      !!!cp ('t107');
4701                        ## NOTE: Whether the encoding is supported or not is handled
4702                        ## in the {change_encoding} callback.
4703                      $self->{change_encoding}                      $self->{change_encoding}
4704                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4705                               $token);
4706                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4707                          ->set_user_data (manakai_has_reference =>                          ->set_user_data (manakai_has_reference =>
4708                                               $token->{attributes}->{content}                                               $token->{attributes}->{content}
# Line 3578  sub _tree_construction_main ($) { Line 4730  sub _tree_construction_main ($) {
4730    
4731                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4732                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4733                  !!!ack ('t110.1');
4734                !!!next-token;                !!!next-token;
4735                redo B;                next B;
4736              } elsif ($token->{tag_name} eq 'title') {              } elsif ($token->{tag_name} eq 'title') {
4737                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4738                  !!!cp ('t111');                  !!!cp ('t111');
4739                  ## As if </noscript>                  ## As if </noscript>
4740                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4741                  !!!parse-error (type => 'in noscript:title', token => $token);                  !!!parse-error (type => 'in noscript', text => 'title',
4742                                    token => $token);
4743                                
4744                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4745                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4746                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4747                  !!!cp ('t112');                  !!!cp ('t112');
4748                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4749                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4750                    push @{$self->{open_elements}},
4751                        [$self->{head_element}, $el_category->{head}];
4752                } else {                } else {
4753                  !!!cp ('t113');                  !!!cp ('t113');
4754                }                }
# Line 3603  sub _tree_construction_main ($) { Line 4759  sub _tree_construction_main ($) {
4759                $parse_rcdata->(RCDATA_CONTENT_MODEL);                $parse_rcdata->(RCDATA_CONTENT_MODEL);
4760                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4761                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4762                redo B;                next B;
4763              } elsif ($token->{tag_name} eq 'style') {              } elsif ($token->{tag_name} eq 'style' or
4764                         $token->{tag_name} eq 'noframes') {
4765                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4766                ## insertion mode IN_HEAD_IM)                ## insertion mode IN_HEAD_IM)
4767                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4768                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4769                  !!!cp ('t114');                  !!!cp ('t114');
4770                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4771                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4772                    push @{$self->{open_elements}},
4773                        [$self->{head_element}, $el_category->{head}];
4774                } else {                } else {
4775                  !!!cp ('t115');                  !!!cp ('t115');
4776                }                }
4777                $parse_rcdata->(CDATA_CONTENT_MODEL);                $parse_rcdata->(CDATA_CONTENT_MODEL);
4778                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4779                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4780                redo B;                next B;
4781              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
4782                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
4783                  !!!cp ('t116');                  !!!cp ('t116');
4784                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
4785                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4786                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4787                    !!!nack ('t116.1');
4788                  !!!next-token;                  !!!next-token;
4789                  redo B;                  next B;
4790                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4791                  !!!cp ('t117');                  !!!cp ('t117');
4792                  !!!parse-error (type => 'in noscript:noscript', token => $token);                  !!!parse-error (type => 'in noscript', text => 'noscript',
4793                                    token => $token);
4794                  ## Ignore the token                  ## Ignore the token
4795                    !!!nack ('t117.1');
4796                  !!!next-token;                  !!!next-token;
4797                  redo B;                  next B;
4798                } else {                } else {
4799                  !!!cp ('t118');                  !!!cp ('t118');
4800                  #                  #
# Line 3642  sub _tree_construction_main ($) { Line 4804  sub _tree_construction_main ($) {
4804                  !!!cp ('t119');                  !!!cp ('t119');
4805                  ## As if </noscript>                  ## As if </noscript>
4806                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4807                  !!!parse-error (type => 'in noscript:script', token => $token);                  !!!parse-error (type => 'in noscript', text => 'script',
4808                                    token => $token);
4809                                
4810                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4811                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4812                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4813                  !!!cp ('t120');                  !!!cp ('t120');
4814                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4815                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4816                    push @{$self->{open_elements}},
4817                        [$self->{head_element}, $el_category->{head}];
4818                } else {                } else {
4819                  !!!cp ('t121');                  !!!cp ('t121');
4820                }                }
# Line 3658  sub _tree_construction_main ($) { Line 4823  sub _tree_construction_main ($) {
4823                $script_start_tag->();                $script_start_tag->();
4824                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4825                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4826                redo B;                next B;
4827              } elsif ($token->{tag_name} eq 'body' or              } elsif ($token->{tag_name} eq 'body' or
4828                       $token->{tag_name} eq 'frameset') {                       $token->{tag_name} eq 'frameset') {
4829                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4830                  !!!cp ('t122');                  !!!cp ('t122');
4831                  ## As if </noscript>                  ## As if </noscript>
4832                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4833                  !!!parse-error (type => 'in noscript:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'in noscript',
4834                                    text => $token->{tag_name}, token => $token);
4835                                    
4836                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4837                  ## As if </head>                  ## As if </head>
# Line 3682  sub _tree_construction_main ($) { Line 4848  sub _tree_construction_main ($) {
4848                }                }
4849    
4850                ## "after head" insertion mode                ## "after head" insertion mode
4851                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4852                if ($token->{tag_name} eq 'body') {                if ($token->{tag_name} eq 'body') {
4853                  !!!cp ('t126');                  !!!cp ('t126');
4854                  $self->{insertion_mode} = IN_BODY_IM;                  $self->{insertion_mode} = IN_BODY_IM;
# Line 3692  sub _tree_construction_main ($) { Line 4858  sub _tree_construction_main ($) {
4858                } else {                } else {
4859                  die "$0: tag name: $self->{tag_name}";                  die "$0: tag name: $self->{tag_name}";
4860                }                }
4861                  !!!nack ('t127.1');
4862                !!!next-token;                !!!next-token;
4863                redo B;                next B;
4864              } else {              } else {
4865                !!!cp ('t128');                !!!cp ('t128');
4866                #                #
# Line 3703  sub _tree_construction_main ($) { Line 4870  sub _tree_construction_main ($) {
4870                !!!cp ('t129');                !!!cp ('t129');
4871                ## As if </noscript>                ## As if </noscript>
4872                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4873                !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'in noscript:/',
4874                                  text => $token->{tag_name}, token => $token);
4875                                
4876                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
4877                ## As if </head>                ## As if </head>
# Line 3722  sub _tree_construction_main ($) { Line 4890  sub _tree_construction_main ($) {
4890    
4891              ## "after head" insertion mode              ## "after head" insertion mode
4892              ## As if <body>              ## As if <body>
4893              !!!insert-element ('body');              !!!insert-element ('body',, $token);
4894              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
4895              ## reprocess              ## reprocess
4896              redo B;              !!!ack-later;
4897                next B;
4898            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
4899              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
4900                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4901                  !!!cp ('t132');                  !!!cp ('t132');
4902                  ## As if <head>                  ## As if <head>
4903                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4904                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4905                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
4906                        [$self->{head_element}, $el_category->{head}];
4907    
4908                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4909                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4910                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4911                  !!!next-token;                  !!!next-token;
4912                  redo B;                  next B;
4913                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4914                  !!!cp ('t133');                  !!!cp ('t133');
4915                  ## As if </noscript>                  ## As if </noscript>
4916                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4917                  !!!parse-error (type => 'in noscript:/head', token => $token);                  !!!parse-error (type => 'in noscript:/',
4918                                    text => 'head', token => $token);
4919                                    
4920                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4921                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4922                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4923                  !!!next-token;                  !!!next-token;
4924                  redo B;                  next B;
4925                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4926                  !!!cp ('t134');                  !!!cp ('t134');
4927                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4928                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4929                  !!!next-token;                  !!!next-token;
4930                  redo B;                  next B;
4931                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4932                    !!!cp ('t134.1');
4933                    !!!parse-error (type => 'unmatched end tag', text => 'head',
4934                                    token => $token);
4935                    ## Ignore the token
4936                    !!!next-token;
4937                    next B;
4938                } else {                } else {
4939                  !!!cp ('t135');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                 #  
4940                }                }
4941              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
4942                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
# Line 3767  sub _tree_construction_main ($) { Line 4944  sub _tree_construction_main ($) {
4944                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4945                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4946                  !!!next-token;                  !!!next-token;
4947                  redo B;                  next B;
4948                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
4949                           $self->{insertion_mode} == AFTER_HEAD_IM) {
4950                  !!!cp ('t137');                  !!!cp ('t137');
4951                  !!!parse-error (type => 'unmatched end tag:noscript', token => $token);                  !!!parse-error (type => 'unmatched end tag',
4952                                    text => 'noscript', token => $token);
4953                  ## Ignore the token ## ISSUE: An issue in the spec.                  ## Ignore the token ## ISSUE: An issue in the spec.
4954                  !!!next-token;                  !!!next-token;
4955                  redo B;                  next B;
4956                } else {                } else {
4957                  !!!cp ('t138');                  !!!cp ('t138');
4958                  #                  #
# Line 3781  sub _tree_construction_main ($) { Line 4960  sub _tree_construction_main ($) {
4960              } elsif ({              } elsif ({
4961                        body => 1, html => 1,                        body => 1, html => 1,
4962                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
4963                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
4964                  !!!cp ('t139');                    $self->{insertion_mode} == IN_HEAD_IM or
4965                  ## As if <head>                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
                 !!!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_IM;  
                 ## Reprocess in the "in head" insertion mode...  
               } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {  
4966                  !!!cp ('t140');                  !!!cp ('t140');
4967                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
4968                                    text => $token->{tag_name}, token => $token);
4969                    ## Ignore the token
4970                    !!!next-token;
4971                    next B;
4972                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4973                    !!!cp ('t140.1');
4974                    !!!parse-error (type => 'unmatched end tag',
4975                                    text => $token->{tag_name}, token => $token);
4976                  ## Ignore the token                  ## Ignore the token
4977                  !!!next-token;                  !!!next-token;
4978                  redo B;                  next B;
4979                } else {                } else {
4980                  !!!cp ('t141');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
4981                }                }
4982                              } elsif ($token->{tag_name} eq 'p') {
4983                #                !!!cp ('t142');
4984              } elsif ({                !!!parse-error (type => 'unmatched end tag',
4985                        p => 1, br => 1,                                text => $token->{tag_name}, token => $token);
4986                       }->{$token->{tag_name}}) {                ## Ignore the token
4987                  !!!next-token;
4988                  next B;
4989                } elsif ($token->{tag_name} eq 'br') {
4990                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4991                  !!!cp ('t142');                  !!!cp ('t142.2');
4992                  ## As if <head>                  ## (before head) as if <head>, (in head) as if </head>
4993                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4994                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4995                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  $self->{insertion_mode} = AFTER_HEAD_IM;
4996      
4997                    ## Reprocess in the "after head" insertion mode...
4998                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4999                    !!!cp ('t143.2');
5000                    ## As if </head>
5001                    pop @{$self->{open_elements}};
5002                    $self->{insertion_mode} = AFTER_HEAD_IM;
5003      
5004                    ## Reprocess in the "after head" insertion mode...
5005                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5006                    !!!cp ('t143.3');
5007                    ## ISSUE: Two parse errors for <head><noscript></br>
5008                    !!!parse-error (type => 'unmatched end tag',
5009                                    text => 'br', token => $token);
5010                    ## As if </noscript>
5011                    pop @{$self->{open_elements}};
5012                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5013    
5014                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5015                } else {                  ## As if </head>
5016                  !!!cp ('t143');                  pop @{$self->{open_elements}};
5017                }                  $self->{insertion_mode} = AFTER_HEAD_IM;
5018    
5019                #                  ## Reprocess in the "after head" insertion mode...
5020              } else {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5021                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                  !!!cp ('t143.4');
                 !!!cp ('t144');  
5022                  #                  #
5023                } else {                } else {
5024                  !!!cp ('t145');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
5025                }                }
5026    
5027                  ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5028                  !!!parse-error (type => 'unmatched end tag',
5029                                  text => 'br', token => $token);
5030                  ## Ignore the token
5031                  !!!next-token;
5032                  next B;
5033                } else {
5034                  !!!cp ('t145');
5035                  !!!parse-error (type => 'unmatched end tag',
5036                                  text => $token->{tag_name}, token => $token);
5037                  ## Ignore the token
5038                  !!!next-token;
5039                  next B;
5040              }              }
5041    
5042              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5043                !!!cp ('t146');                !!!cp ('t146');
5044                ## As if </noscript>                ## As if </noscript>
5045                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5046                !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'in noscript:/',
5047                                  text => $token->{tag_name}, token => $token);
5048                                
5049                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
5050                ## As if </head>                ## As if </head>
# Line 3851  sub _tree_construction_main ($) { Line 5060  sub _tree_construction_main ($) {
5060              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5061  ## ISSUE: This case cannot be reached?  ## ISSUE: This case cannot be reached?
5062                !!!cp ('t148');                !!!cp ('t148');
5063                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
5064                                  text => $token->{tag_name}, token => $token);
5065                ## Ignore the token ## ISSUE: An issue in the spec.                ## Ignore the token ## ISSUE: An issue in the spec.
5066                !!!next-token;                !!!next-token;
5067                redo B;                next B;
5068              } else {              } else {
5069                !!!cp ('t149');                !!!cp ('t149');
5070              }              }
5071    
5072              ## "after head" insertion mode              ## "after head" insertion mode
5073              ## As if <body>              ## As if <body>
5074              !!!insert-element ('body');              !!!insert-element ('body',, $token);
5075              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
5076              ## reprocess              ## reprocess
5077              redo B;              next B;
5078        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5079          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5080            !!!cp ('t149.1');            !!!cp ('t149.1');
5081    
5082            ## NOTE: As if <head>            ## NOTE: As if <head>
5083            !!!create-element ($self->{head_element}, 'head');            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5084            $self->{open_elements}->[-1]->[0]->append_child            $self->{open_elements}->[-1]->[0]->append_child
5085                ($self->{head_element});                ($self->{head_element});
5086            #push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            #push @{$self->{open_elements}},
5087              #    [$self->{head_element}, $el_category->{head}];
5088            #$self->{insertion_mode} = IN_HEAD_IM;            #$self->{insertion_mode} = IN_HEAD_IM;
5089            ## NOTE: Reprocess.            ## NOTE: Reprocess.
5090    
# Line 3914  sub _tree_construction_main ($) { Line 5125  sub _tree_construction_main ($) {
5125          }          }
5126    
5127          ## NOTE: As if <body>          ## NOTE: As if <body>
5128          !!!insert-element ('body');          !!!insert-element ('body',, $token);
5129          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
5130          ## NOTE: Reprocess.          ## NOTE: Reprocess.
5131          redo B;          next B;
5132        } else {        } else {
5133          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5134        }        }
# Line 3932  sub _tree_construction_main ($) { Line 5143  sub _tree_construction_main ($) {
5143              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5144    
5145              !!!next-token;              !!!next-token;
5146              redo B;              next B;
5147            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
5148              if ({              if ({
5149                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
# Line 3942  sub _tree_construction_main ($) { Line 5153  sub _tree_construction_main ($) {
5153                  ## have an element in table scope                  ## have an element in table scope
5154                  for (reverse 0..$#{$self->{open_elements}}) {                  for (reverse 0..$#{$self->{open_elements}}) {
5155                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5156                    if ($node->[1] eq 'td' or $node->[1] eq 'th') {                    if ($node->[1] & TABLE_CELL_EL) {
5157                      !!!cp ('t151');                      !!!cp ('t151');
5158    
5159                      ## Close the cell                      ## Close the cell
5160                      !!!back-token; # <?>                      !!!back-token; # <x>
5161                      $token = {type => END_TAG_TOKEN, tag_name => $node->[1]};                      $token = {type => END_TAG_TOKEN,
5162                      redo B;                                tag_name => $node->[0]->manakai_local_name,
5163                    } elsif ({                                line => $token->{line},
5164                              table => 1, html => 1,                                column => $token->{column}};
5165                             }->{$node->[1]}) {                      next B;
5166                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5167                      !!!cp ('t152');                      !!!cp ('t152');
5168                      ## ISSUE: This case can never be reached, maybe.                      ## ISSUE: This case can never be reached, maybe.
5169                      last;                      last;
# Line 3960  sub _tree_construction_main ($) { Line 5172  sub _tree_construction_main ($) {
5172    
5173                  !!!cp ('t153');                  !!!cp ('t153');
5174                  !!!parse-error (type => 'start tag not allowed',                  !!!parse-error (type => 'start tag not allowed',
5175                      value => $token->{tag_name}, token => $token);                      text => $token->{tag_name}, token => $token);
5176                  ## Ignore the token                  ## Ignore the token
5177                    !!!nack ('t153.1');
5178                  !!!next-token;                  !!!next-token;
5179                  redo B;                  next B;
5180                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5181                  !!!parse-error (type => 'not closed:caption', token => $token);                  !!!parse-error (type => 'not closed', text => 'caption',
5182                                    token => $token);
5183                                    
5184                  ## NOTE: As if </caption>.                  ## NOTE: As if </caption>.
5185                  ## have a table element in table scope                  ## have a table element in table scope
# Line 3973  sub _tree_construction_main ($) { Line 5187  sub _tree_construction_main ($) {
5187                  INSCOPE: {                  INSCOPE: {
5188                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
5189                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
5190                      if ($node->[1] eq 'caption') {                      if ($node->[1] & CAPTION_EL) {
5191                        !!!cp ('t155');                        !!!cp ('t155');
5192                        $i = $_;                        $i = $_;
5193                        last INSCOPE;                        last INSCOPE;
5194                      } elsif ({                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
                               table => 1, html => 1,  
                              }->{$node->[1]}) {  
5195                        !!!cp ('t156');                        !!!cp ('t156');
5196                        last;                        last;
5197                      }                      }
# Line 3987  sub _tree_construction_main ($) { Line 5199  sub _tree_construction_main ($) {
5199    
5200                    !!!cp ('t157');                    !!!cp ('t157');
5201                    !!!parse-error (type => 'start tag not allowed',                    !!!parse-error (type => 'start tag not allowed',
5202                                    value => $token->{tag_name}, token => $token);                                    text => $token->{tag_name}, token => $token);
5203                    ## Ignore the token                    ## Ignore the token
5204                      !!!nack ('t157.1');
5205                    !!!next-token;                    !!!next-token;
5206                    redo B;                    next B;
5207                  } # INSCOPE                  } # INSCOPE
5208                                    
5209                  ## generate implied end tags                  ## generate implied end tags
5210                  while ({                  while ($self->{open_elements}->[-1]->[1]
5211                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5212                    !!!cp ('t158');                    !!!cp ('t158');
5213                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5214                  }                  }
5215    
5216                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5217                    !!!cp ('t159');                    !!!cp ('t159');
5218                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                    !!!parse-error (type => 'not closed',
5219                                      text => $self->{open_elements}->[-1]->[0]
5220                                          ->manakai_local_name,
5221                                      token => $token);
5222                  } else {                  } else {
5223                    !!!cp ('t160');                    !!!cp ('t160');
5224                  }                  }
# Line 4015  sub _tree_construction_main ($) { Line 5230  sub _tree_construction_main ($) {
5230                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5231                                    
5232                  ## reprocess                  ## reprocess
5233                  redo B;                  !!!ack-later;
5234                    next B;
5235                } else {                } else {
5236                  !!!cp ('t161');                  !!!cp ('t161');
5237                  #                  #
# Line 4031  sub _tree_construction_main ($) { Line 5247  sub _tree_construction_main ($) {
5247                  my $i;                  my $i;
5248                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5249                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5250                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5251                      !!!cp ('t163');                      !!!cp ('t163');
5252                      $i = $_;                      $i = $_;
5253                      last INSCOPE;                      last INSCOPE;
5254                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5255                      !!!cp ('t164');                      !!!cp ('t164');
5256                      last INSCOPE;                      last INSCOPE;
5257                    }                    }
5258                  } # INSCOPE                  } # INSCOPE
5259                    unless (defined $i) {                    unless (defined $i) {
5260                      !!!cp ('t165');                      !!!cp ('t165');
5261                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                      !!!parse-error (type => 'unmatched end tag',
5262                                        text => $token->{tag_name},
5263                                        token => $token);
5264                      ## Ignore the token                      ## Ignore the token
5265                      !!!next-token;                      !!!next-token;
5266                      redo B;                      next B;
5267                    }                    }
5268                                    
5269                  ## generate implied end tags                  ## generate implied end tags
5270                  while ({                  while ($self->{open_elements}->[-1]->[1]
5271                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5272                    !!!cp ('t166');                    !!!cp ('t166');
5273                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5274                  }                  }
5275    
5276                  if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5277                            ne $token->{tag_name}) {
5278                    !!!cp ('t167');                    !!!cp ('t167');
5279                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                    !!!parse-error (type => 'not closed',
5280                                      text => $self->{open_elements}->[-1]->[0]
5281                                          ->manakai_local_name,
5282                                      token => $token);
5283                  } else {                  } else {
5284                    !!!cp ('t168');                    !!!cp ('t168');
5285                  }                  }
# Line 4072  sub _tree_construction_main ($) { Line 5291  sub _tree_construction_main ($) {
5291                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5292                                    
5293                  !!!next-token;                  !!!next-token;
5294                  redo B;                  next B;
5295                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5296                  !!!cp ('t169');                  !!!cp ('t169');
5297                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5298                                    text => $token->{tag_name}, token => $token);
5299                  ## Ignore the token                  ## Ignore the token
5300                  !!!next-token;                  !!!next-token;
5301                  redo B;                  next B;
5302                } else {                } else {
5303                  !!!cp ('t170');                  !!!cp ('t170');
5304                  #                  #
# Line 4090  sub _tree_construction_main ($) { Line 5310  sub _tree_construction_main ($) {
5310                  INSCOPE: {                  INSCOPE: {
5311                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
5312                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
5313                      if ($node->[1] eq $token->{tag_name}) {                      if ($node->[1] & CAPTION_EL) {
5314                        !!!cp ('t171');                        !!!cp ('t171');
5315                        $i = $_;                        $i = $_;
5316                        last INSCOPE;                        last INSCOPE;
5317                      } elsif ({                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
                               table => 1, html => 1,  
                              }->{$node->[1]}) {  
5318                        !!!cp ('t172');                        !!!cp ('t172');
5319                        last;                        last;
5320                      }                      }
# Line 4104  sub _tree_construction_main ($) { Line 5322  sub _tree_construction_main ($) {
5322    
5323                    !!!cp ('t173');                    !!!cp ('t173');
5324                    !!!parse-error (type => 'unmatched end tag',                    !!!parse-error (type => 'unmatched end tag',
5325                                    value => $token->{tag_name}, token => $token);                                    text => $token->{tag_name}, token => $token);
5326                    ## Ignore the token                    ## Ignore the token
5327                    !!!next-token;                    !!!next-token;
5328                    redo B;                    next B;
5329                  } # INSCOPE                  } # INSCOPE
5330                                    
5331                  ## generate implied end tags                  ## generate implied end tags
5332                  while ({                  while ($self->{open_elements}->[-1]->[1]
5333                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5334                    !!!cp ('t174');                    !!!cp ('t174');
5335                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5336                  }                  }
5337                                    
5338                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5339                    !!!cp ('t175');                    !!!cp ('t175');
5340                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                    !!!parse-error (type => 'not closed',
5341                                      text => $self->{open_elements}->[-1]->[0]
5342                                          ->manakai_local_name,
5343                                      token => $token);
5344                  } else {                  } else {
5345                    !!!cp ('t176');                    !!!cp ('t176');
5346                  }                  }
# Line 4132  sub _tree_construction_main ($) { Line 5352  sub _tree_construction_main ($) {
5352                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5353                                    
5354                  !!!next-token;                  !!!next-token;
5355                  redo B;                  next B;
5356                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5357                  !!!cp ('t177');                  !!!cp ('t177');
5358                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5359                                    text => $token->{tag_name}, token => $token);
5360                  ## Ignore the token                  ## Ignore the token
5361                  !!!next-token;                  !!!next-token;
5362                  redo B;                  next B;
5363                } else {                } else {
5364                  !!!cp ('t178');                  !!!cp ('t178');
5365                  #                  #
# Line 4154  sub _tree_construction_main ($) { Line 5375  sub _tree_construction_main ($) {
5375                INSCOPE: {                INSCOPE: {
5376                  for (reverse 0..$#{$self->{open_elements}}) {                  for (reverse 0..$#{$self->{open_elements}}) {
5377                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5378                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5379                      !!!cp ('t179');                      !!!cp ('t179');
5380                      $i = $_;                      $i = $_;
5381    
5382                      ## Close the cell                      ## Close the cell
5383                      !!!back-token; # </?>                      !!!back-token; # </x>
5384                      $token = {type => END_TAG_TOKEN, tag_name => $tn};                      $token = {type => END_TAG_TOKEN, tag_name => $tn,
5385                      redo B;                                line => $token->{line},
5386                    } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {                                column => $token->{column}};
5387                        next B;
5388                      } elsif ($node->[1] & TABLE_CELL_EL) {
5389                      !!!cp ('t180');                      !!!cp ('t180');
5390                      $tn = $node->[1];                      $tn = $node->[0]->manakai_local_name;
5391                      ## NOTE: There is exactly one |td| or |th| element                      ## NOTE: There is exactly one |td| or |th| element
5392                      ## in scope in the stack of open elements by definition.                      ## in scope in the stack of open elements by definition.
5393                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5394                      ## ISSUE: Can this be reached?                      ## ISSUE: Can this be reached?
5395                      !!!cp ('t181');                      !!!cp ('t181');
5396                      last;                      last;
# Line 4178  sub _tree_construction_main ($) { Line 5399  sub _tree_construction_main ($) {
5399    
5400                  !!!cp ('t182');                  !!!cp ('t182');
5401                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
5402                      value => $token->{tag_name}, token => $token);                      text => $token->{tag_name}, token => $token);
5403                  ## Ignore the token                  ## Ignore the token
5404                  !!!next-token;                  !!!next-token;
5405                  redo B;                  next B;
5406                } # INSCOPE                } # INSCOPE
5407              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
5408                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5409                !!!parse-error (type => 'not closed:caption', token => $token);                !!!parse-error (type => 'not closed', text => 'caption',
5410                                  token => $token);
5411    
5412                ## As if </caption>                ## As if </caption>
5413                ## have a table element in table scope                ## have a table element in table scope
5414                my $i;                my $i;
5415                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5416                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5417                  if ($node->[1] eq 'caption') {                  if ($node->[1] & CAPTION_EL) {
5418                    !!!cp ('t184');                    !!!cp ('t184');
5419                    $i = $_;                    $i = $_;
5420                    last INSCOPE;                    last INSCOPE;
5421                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
5422                    !!!cp ('t185');                    !!!cp ('t185');
5423                    last INSCOPE;                    last INSCOPE;
5424                  }                  }
5425                } # INSCOPE                } # INSCOPE
5426                unless (defined $i) {                unless (defined $i) {
5427                  !!!cp ('t186');                  !!!cp ('t186');
5428                  !!!parse-error (type => 'unmatched end tag:caption', token => $token);                  !!!parse-error (type => 'unmatched end tag',
5429                                    text => 'caption', token => $token);
5430                  ## Ignore the token                  ## Ignore the token
5431                  !!!next-token;                  !!!next-token;
5432                  redo B;                  next B;
5433                }                }
5434                                
5435                ## generate implied end tags                ## generate implied end tags
5436                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
5437                  !!!cp ('t187');                  !!!cp ('t187');
5438                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5439                }                }
5440    
5441                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5442                  !!!cp ('t188');                  !!!cp ('t188');
5443                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                  !!!parse-error (type => 'not closed',
5444                                    text => $self->{open_elements}->[-1]->[0]
5445                                        ->manakai_local_name,
5446                                    token => $token);
5447                } else {                } else {
5448                  !!!cp ('t189');                  !!!cp ('t189');
5449                }                }
# Line 4233  sub _tree_construction_main ($) { Line 5455  sub _tree_construction_main ($) {
5455                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
5456    
5457                ## reprocess                ## reprocess
5458                redo B;                next B;
5459              } elsif ({              } elsif ({
5460                        body => 1, col => 1, colgroup => 1, html => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
5461                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5462                if ($self->{insertion_mode} & BODY_TABLE_IMS) {                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5463                  !!!cp ('t190');                  !!!cp ('t190');
5464                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5465                                    text => $token->{tag_name}, token => $token);
5466                  ## Ignore the token                  ## Ignore the token
5467                  !!!next-token;                  !!!next-token;
5468                  redo B;                  next B;
5469                } else {                } else {
5470                  !!!cp ('t191');                  !!!cp ('t191');
5471                  #                  #
# Line 4253  sub _tree_construction_main ($) { Line 5476  sub _tree_construction_main ($) {
5476                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5477                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5478                !!!cp ('t192');                !!!cp ('t192');
5479                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
5480                                  text => $token->{tag_name}, token => $token);
5481                ## Ignore the token                ## Ignore the token
5482                !!!next-token;                !!!next-token;
5483                redo B;                next B;
5484              } else {              } else {
5485                !!!cp ('t193');                !!!cp ('t193');
5486                #                #
5487              }              }
5488        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5489          for my $entry (@{$self->{open_elements}}) {          for my $entry (@{$self->{open_elements}}) {
5490            if (not {            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
             dd => 1, dt => 1, li => 1, p => 1, tbody => 1, td => 1, tfoot => 1,  
             th => 1, thead => 1, tr => 1, body => 1, html => 1,  
           }->{$entry->[1]}) {  
5491              !!!cp ('t75');              !!!cp ('t75');
5492              !!!parse-error (type => 'in body:#eof', token => $token);              !!!parse-error (type => 'in body:#eof', token => $token);
5493              last;              last;
# Line 4284  sub _tree_construction_main ($) { Line 5505  sub _tree_construction_main ($) {
5505      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
5506        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
5507          if (not $open_tables->[-1]->[1] and # tainted          if (not $open_tables->[-1]->[1] and # tainted
5508              $token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5509            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5510                                
5511            unless (length $token->{data}) {            unless (length $token->{data}) {
5512              !!!cp ('t194');              !!!cp ('t194');
5513              !!!next-token;              !!!next-token;
5514              redo B;              next B;
5515            } else {            } else {
5516              !!!cp ('t195');              !!!cp ('t195');
5517            }            }
5518          }          }
5519    
5520              !!!parse-error (type => 'in table:#character', token => $token);          !!!parse-error (type => 'in table:#text', token => $token);
5521    
5522              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
5523              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
# Line 4304  sub _tree_construction_main ($) { Line 5525  sub _tree_construction_main ($) {
5525              ## result in a new Text node.              ## result in a new Text node.
5526              $reconstruct_active_formatting_elements->($insert_to_foster);              $reconstruct_active_formatting_elements->($insert_to_foster);
5527                            
5528              if ({              if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
                  table => 1, tbody => 1, tfoot => 1,  
                  thead => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5529                # MUST                # MUST
5530                my $foster_parent_element;                my $foster_parent_element;
5531                my $next_sibling;                my $next_sibling;
5532                my $prev_sibling;                my $prev_sibling;
5533                OE: for (reverse 0..$#{$self->{open_elements}}) {                OE: for (reverse 0..$#{$self->{open_elements}}) {
5534                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5535                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5536                    if (defined $parent and $parent->node_type == 1) {                    if (defined $parent and $parent->node_type == 1) {
5537                      !!!cp ('t196');                      !!!cp ('t196');
# Line 4348  sub _tree_construction_main ($) { Line 5566  sub _tree_construction_main ($) {
5566          }          }
5567                            
5568          !!!next-token;          !!!next-token;
5569          redo B;          next B;
5570        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5571              if ({          if ({
5572                   tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => ($self->{insertion_mode} != IN_ROW_IM),
5573                   th => 1, td => 1,               th => 1, td => 1,
5574                  }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
5575                if ($self->{insertion_mode} == IN_TABLE_IM) {            if ($self->{insertion_mode} == IN_TABLE_IM) {
5576                  ## Clear back to table context              ## Clear back to table context
5577                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5578                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5579                    !!!cp ('t201');                !!!cp ('t201');
5580                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5581                  }              }
5582                                
5583                  !!!insert-element ('tbody');              !!!insert-element ('tbody',, $token);
5584                  $self->{insertion_mode} = IN_TABLE_BODY_IM;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5585                  ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
5586                }            }
5587              
5588                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5589                  unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
5590                    !!!cp ('t202');                !!!cp ('t202');
5591                    !!!parse-error (type => 'missing start tag:tr', token => $token);                !!!parse-error (type => 'missing start tag:tr', token => $token);
5592                  }              }
5593                                    
5594                  ## Clear back to table body context              ## Clear back to table body context
5595                  while (not {              while (not ($self->{open_elements}->[-1]->[1]
5596                    tbody => 1, tfoot => 1, thead => 1, html => 1,                              & TABLE_ROWS_SCOPING_EL)) {
5597                  }->{$self->{open_elements}->[-1]->[1]}) {                !!!cp ('t203');
5598                    !!!cp ('t203');                ## ISSUE: Can this case be reached?
5599                    ## ISSUE: Can this case be reached?                pop @{$self->{open_elements}};
5600                    pop @{$self->{open_elements}};              }
                 }  
5601                                    
5602                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5603                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
5604                    !!!cp ('t204');                    !!!cp ('t204');
5605                    !!!insert-element ($token->{tag_name}, $token->{attributes});                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5606                      !!!nack ('t204');
5607                    !!!next-token;                    !!!next-token;
5608                    redo B;                    next B;
5609                  } else {                  } else {
5610                    !!!cp ('t205');                    !!!cp ('t205');
5611                    !!!insert-element ('tr');                    !!!insert-element ('tr',, $token);
5612                    ## reprocess in the "in row" insertion mode                    ## reprocess in the "in row" insertion mode
5613                  }                  }
5614                } else {                } else {
# Line 4398  sub _tree_construction_main ($) { Line 5616  sub _tree_construction_main ($) {
5616                }                }
5617    
5618                ## Clear back to table row context                ## Clear back to table row context
5619                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5620                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
5621                  !!!cp ('t207');                  !!!cp ('t207');
5622                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5623                }                }
5624                                
5625                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5626                $self->{insertion_mode} = IN_CELL_IM;                $self->{insertion_mode} = IN_CELL_IM;
5627    
5628                push @$active_formatting_elements, ['#marker', ''];                push @$active_formatting_elements, ['#marker', ''];
5629                                
5630                  !!!nack ('t207.1');
5631                !!!next-token;                !!!next-token;
5632                redo B;                next B;
5633              } elsif ({              } elsif ({
5634                        caption => 1, col => 1, colgroup => 1,                        caption => 1, col => 1, colgroup => 1,
5635                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
# Line 4423  sub _tree_construction_main ($) { Line 5641  sub _tree_construction_main ($) {
5641                  my $i;                  my $i;
5642                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5643                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5644                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
5645                      !!!cp ('t208');                      !!!cp ('t208');
5646                      $i = $_;                      $i = $_;
5647                      last INSCOPE;                      last INSCOPE;
5648                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             html => 1,  
   
                             ## NOTE: This element does not appear here, maybe.  
                             table => 1,  
                            }->{$node->[1]}) {  
5649                      !!!cp ('t209');                      !!!cp ('t209');
5650                      last INSCOPE;                      last INSCOPE;
5651                    }                    }
5652                  } # INSCOPE                  } # INSCOPE
5653                  unless (defined $i) {                  unless (defined $i) {
5654                   !!!cp ('t210');                    !!!cp ('t210');
5655  ## TODO: This type is wrong.  ## TODO: This type is wrong.
5656                   !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name}, token => $token);                    !!!parse-error (type => 'unmacthed end tag',
5657                                      text => $token->{tag_name}, token => $token);
5658                    ## Ignore the token                    ## Ignore the token
5659                      !!!nack ('t210.1');
5660                    !!!next-token;                    !!!next-token;
5661                    redo B;                    next B;
5662                  }                  }
5663                                    
5664                  ## Clear back to table row context                  ## Clear back to table row context
5665                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5666                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5667                    !!!cp ('t211');                    !!!cp ('t211');
5668                    ## ISSUE: Can this case be reached?                    ## ISSUE: Can this case be reached?
5669                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4460  sub _tree_construction_main ($) { Line 5674  sub _tree_construction_main ($) {
5674                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
5675                    !!!cp ('t212');                    !!!cp ('t212');
5676                    ## reprocess                    ## reprocess
5677                    redo B;                    !!!ack-later;
5678                      next B;
5679                  } else {                  } else {
5680                    !!!cp ('t213');                    !!!cp ('t213');
5681                    ## reprocess in the "in table body" insertion mode...                    ## reprocess in the "in table body" insertion mode...
# Line 4472  sub _tree_construction_main ($) { Line 5687  sub _tree_construction_main ($) {
5687                  my $i;                  my $i;
5688                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5689                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5690                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
5691                      !!!cp ('t214');                      !!!cp ('t214');
5692                      $i = $_;                      $i = $_;
5693                      last INSCOPE;                      last INSCOPE;
5694                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5695                      !!!cp ('t215');                      !!!cp ('t215');
5696                      last INSCOPE;                      last INSCOPE;
5697                    }                    }
5698                  } # INSCOPE                  } # INSCOPE
5699                  unless (defined $i) {                  unless (defined $i) {
5700                    !!!cp ('t216');                    !!!cp ('t216');
5701  ## TODO: This erorr type ios wrong.  ## TODO: This erorr type is wrong.
5702                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                    !!!parse-error (type => 'unmatched end tag',
5703                                      text => $token->{tag_name}, token => $token);
5704                    ## Ignore the token                    ## Ignore the token
5705                      !!!nack ('t216.1');
5706                    !!!next-token;                    !!!next-token;
5707                    redo B;                    next B;
5708                  }                  }
5709    
5710                  ## Clear back to table body context                  ## Clear back to table body context
5711                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5712                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5713                    !!!cp ('t217');                    !!!cp ('t217');
5714                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
5715                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4519  sub _tree_construction_main ($) { Line 5731  sub _tree_construction_main ($) {
5731    
5732                if ($token->{tag_name} eq 'col') {                if ($token->{tag_name} eq 'col') {
5733                  ## Clear back to table context                  ## Clear back to table context
5734                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                  while (not ($self->{open_elements}->[-1]->[1]
5735                         $self->{open_elements}->[-1]->[1] ne 'html') {                                  & TABLE_SCOPING_EL)) {
5736                    !!!cp ('t219');                    !!!cp ('t219');
5737                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
5738                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5739                  }                  }
5740                                    
5741                  !!!insert-element ('colgroup');                  !!!insert-element ('colgroup',, $token);
5742                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5743                  ## reprocess                  ## reprocess
5744                  redo B;                  !!!ack-later;
5745                    next B;
5746                } elsif ({                } elsif ({
5747                          caption => 1,                          caption => 1,
5748                          colgroup => 1,                          colgroup => 1,
5749                          tbody => 1, tfoot => 1, thead => 1,                          tbody => 1, tfoot => 1, thead => 1,
5750                         }->{$token->{tag_name}}) {                         }->{$token->{tag_name}}) {
5751                  ## Clear back to table context                  ## Clear back to table context
5752                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                  while (not ($self->{open_elements}->[-1]->[1]
5753                         $self->{open_elements}->[-1]->[1] ne 'html') {                                  & TABLE_SCOPING_EL)) {
5754                    !!!cp ('t220');                    !!!cp ('t220');
5755                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
5756                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4546  sub _tree_construction_main ($) { Line 5759  sub _tree_construction_main ($) {
5759                  push @$active_formatting_elements, ['#marker', '']                  push @$active_formatting_elements, ['#marker', '']
5760                      if $token->{tag_name} eq 'caption';                      if $token->{tag_name} eq 'caption';
5761                                    
5762                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5763                  $self->{insertion_mode} = {                  $self->{insertion_mode} = {
5764                                             caption => IN_CAPTION_IM,                                             caption => IN_CAPTION_IM,
5765                                             colgroup => IN_COLUMN_GROUP_IM,                                             colgroup => IN_COLUMN_GROUP_IM,
# Line 4555  sub _tree_construction_main ($) { Line 5768  sub _tree_construction_main ($) {
5768                                             thead => IN_TABLE_BODY_IM,                                             thead => IN_TABLE_BODY_IM,
5769                                            }->{$token->{tag_name}};                                            }->{$token->{tag_name}};
5770                  !!!next-token;                  !!!next-token;
5771                  redo B;                  !!!nack ('t220.1');
5772                    next B;
5773                } else {                } else {
5774                  die "$0: in table: <>: $token->{tag_name}";                  die "$0: in table: <>: $token->{tag_name}";
5775                }                }
5776              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5777                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                !!!parse-error (type => 'not closed',
5778                                  text => $self->{open_elements}->[-1]->[0]
5779                                      ->manakai_local_name,
5780                                  token => $token);
5781    
5782                ## As if </table>                ## As if </table>
5783                ## have a table element in table scope                ## have a table element in table scope
5784                my $i;                my $i;
5785                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5786                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5787                  if ($node->[1] eq 'table') {                  if ($node->[1] & TABLE_EL) {
5788                    !!!cp ('t221');                    !!!cp ('t221');
5789                    $i = $_;                    $i = $_;
5790                    last INSCOPE;                    last INSCOPE;
5791                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           #table => 1,  
                           html => 1,  
                          }->{$node->[1]}) {  
5792                    !!!cp ('t222');                    !!!cp ('t222');
5793                    last INSCOPE;                    last INSCOPE;
5794                  }                  }
# Line 4582  sub _tree_construction_main ($) { Line 5796  sub _tree_construction_main ($) {
5796                unless (defined $i) {                unless (defined $i) {
5797                  !!!cp ('t223');                  !!!cp ('t223');
5798  ## TODO: The following is wrong, maybe.  ## TODO: The following is wrong, maybe.
5799                  !!!parse-error (type => 'unmatched end tag:table', token => $token);                  !!!parse-error (type => 'unmatched end tag', text => 'table',
5800                                    token => $token);
5801                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5802                    !!!nack ('t223.1');
5803                  !!!next-token;                  !!!next-token;
5804                  redo B;                  next B;
5805                }                }
5806                                
5807  ## TODO: Followings are removed from the latest spec.  ## TODO: Followings are removed from the latest spec.
5808                ## generate implied end tags                ## generate implied end tags
5809                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
5810                  !!!cp ('t224');                  !!!cp ('t224');
5811                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5812                }                }
5813    
5814                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
5815                  !!!cp ('t225');                  !!!cp ('t225');
5816  ## ISSUE: Can this case be reached?                  ## NOTE: |<table><tr><table>|
5817                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                  !!!parse-error (type => 'not closed',
5818                                    text => $self->{open_elements}->[-1]->[0]
5819                                        ->manakai_local_name,
5820                                    token => $token);
5821                } else {                } else {
5822                  !!!cp ('t226');                  !!!cp ('t226');
5823                }                }
# Line 4610  sub _tree_construction_main ($) { Line 5827  sub _tree_construction_main ($) {
5827    
5828                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5829    
5830                ## reprocess            ## reprocess
5831                redo B;            !!!ack-later;
5832              next B;
5833          } elsif ($token->{tag_name} eq 'style') {          } elsif ($token->{tag_name} eq 'style') {
5834            if (not $open_tables->[-1]->[1]) { # tainted            if (not $open_tables->[-1]->[1]) { # tainted
5835              !!!cp ('t227.8');              !!!cp ('t227.8');
5836              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
5837              $parse_rcdata->(CDATA_CONTENT_MODEL);              $parse_rcdata->(CDATA_CONTENT_MODEL);
5838              redo B;              next B;
5839            } else {            } else {
5840              !!!cp ('t227.7');              !!!cp ('t227.7');
5841              #              #
# Line 4627  sub _tree_construction_main ($) { Line 5845  sub _tree_construction_main ($) {
5845              !!!cp ('t227.6');              !!!cp ('t227.6');
5846              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
5847              $script_start_tag->();              $script_start_tag->();
5848              redo B;              next B;
5849            } else {            } else {
5850              !!!cp ('t227.5');              !!!cp ('t227.5');
5851              #              #
# Line 4638  sub _tree_construction_main ($) { Line 5856  sub _tree_construction_main ($) {
5856                my $type = lc $token->{attributes}->{type}->{value};                my $type = lc $token->{attributes}->{type}->{value};
5857                if ($type eq 'hidden') {                if ($type eq 'hidden') {
5858                  !!!cp ('t227.3');                  !!!cp ('t227.3');
5859                  !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'in table',
5860                                    text => $token->{tag_name}, token => $token);
5861    
5862                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5863    
5864                  ## TODO: form element pointer                  ## TODO: form element pointer
5865    
5866                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5867    
5868                  !!!next-token;                  !!!next-token;
5869                  redo B;                  !!!ack ('t227.2.1');
5870                    next B;
5871                } else {                } else {
5872                  !!!cp ('t227.2');                  !!!cp ('t227.2');
5873                  #                  #
# Line 4665  sub _tree_construction_main ($) { Line 5885  sub _tree_construction_main ($) {
5885            #            #
5886          }          }
5887    
5888          !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);          !!!parse-error (type => 'in table', text => $token->{tag_name},
5889                            token => $token);
5890    
5891          $insert = $insert_to_foster;          $insert = $insert_to_foster;
5892          #          #
# Line 4676  sub _tree_construction_main ($) { Line 5897  sub _tree_construction_main ($) {
5897                my $i;                my $i;
5898                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5899                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5900                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_ROW_EL) {
5901                    !!!cp ('t228');                    !!!cp ('t228');
5902                    $i = $_;                    $i = $_;
5903                    last INSCOPE;                    last INSCOPE;
5904                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
5905                    !!!cp ('t229');                    !!!cp ('t229');
5906                    last INSCOPE;                    last INSCOPE;
5907                  }                  }
5908                } # INSCOPE                } # INSCOPE
5909                unless (defined $i) {                unless (defined $i) {
5910                  !!!cp ('t230');                  !!!cp ('t230');
5911                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5912                                    text => $token->{tag_name}, token => $token);
5913                  ## Ignore the token                  ## Ignore the token
5914                    !!!nack ('t230.1');
5915                  !!!next-token;                  !!!next-token;
5916                  redo B;                  next B;
5917                } else {                } else {
5918                  !!!cp ('t232');                  !!!cp ('t232');
5919                }                }
5920    
5921                ## Clear back to table row context                ## Clear back to table row context
5922                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5923                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
5924                  !!!cp ('t231');                  !!!cp ('t231');
5925  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
5926                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
# Line 4709  sub _tree_construction_main ($) { Line 5929  sub _tree_construction_main ($) {
5929                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
5930                $self->{insertion_mode} = IN_TABLE_BODY_IM;                $self->{insertion_mode} = IN_TABLE_BODY_IM;
5931                !!!next-token;                !!!next-token;
5932                redo B;                !!!nack ('t231.1');
5933                  next B;
5934              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5935                if ($self->{insertion_mode} == IN_ROW_IM) {                if ($self->{insertion_mode} == IN_ROW_IM) {
5936                  ## As if </tr>                  ## As if </tr>
# Line 4717  sub _tree_construction_main ($) { Line 5938  sub _tree_construction_main ($) {
5938                  my $i;                  my $i;
5939                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5940                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5941                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
5942                      !!!cp ('t233');                      !!!cp ('t233');
5943                      $i = $_;                      $i = $_;
5944                      last INSCOPE;                      last INSCOPE;
5945                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5946                      !!!cp ('t234');                      !!!cp ('t234');
5947                      last INSCOPE;                      last INSCOPE;
5948                    }                    }
# Line 4731  sub _tree_construction_main ($) { Line 5950  sub _tree_construction_main ($) {
5950                  unless (defined $i) {                  unless (defined $i) {
5951                    !!!cp ('t235');                    !!!cp ('t235');
5952  ## TODO: The following is wrong.  ## TODO: The following is wrong.
5953                    !!!parse-error (type => 'unmatched end tag:'.$token->{type}, token => $token);                    !!!parse-error (type => 'unmatched end tag',
5954                                      text => $token->{type}, token => $token);
5955                    ## Ignore the token                    ## Ignore the token
5956                      !!!nack ('t236.1');
5957                    !!!next-token;                    !!!next-token;
5958                    redo B;                    next B;
5959                  }                  }
5960                                    
5961                  ## Clear back to table row context                  ## Clear back to table row context
5962                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5963                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5964                    !!!cp ('t236');                    !!!cp ('t236');
5965  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
5966                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4756  sub _tree_construction_main ($) { Line 5976  sub _tree_construction_main ($) {
5976                  my $i;                  my $i;
5977                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5978                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5979                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
5980                      !!!cp ('t237');                      !!!cp ('t237');
5981                      $i = $_;                      $i = $_;
5982                      last INSCOPE;                      last INSCOPE;
5983                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5984                      !!!cp ('t238');                      !!!cp ('t238');
5985                      last INSCOPE;                      last INSCOPE;
5986                    }                    }
5987                  } # INSCOPE                  } # INSCOPE
5988                  unless (defined $i) {                  unless (defined $i) {
5989                    !!!cp ('t239');                    !!!cp ('t239');
5990                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                    !!!parse-error (type => 'unmatched end tag',
5991                                      text => $token->{tag_name}, token => $token);
5992                    ## Ignore the token                    ## Ignore the token
5993                      !!!nack ('t239.1');
5994                    !!!next-token;                    !!!next-token;
5995                    redo B;                    next B;
5996                  }                  }
5997                                    
5998                  ## Clear back to table body context                  ## Clear back to table body context
5999                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6000                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6001                    !!!cp ('t240');                    !!!cp ('t240');
6002                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6003                  }                  }
# Line 4806  sub _tree_construction_main ($) { Line 6023  sub _tree_construction_main ($) {
6023                my $i;                my $i;
6024                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6025                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6026                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_EL) {
6027                    !!!cp ('t241');                    !!!cp ('t241');
6028                    $i = $_;                    $i = $_;
6029                    last INSCOPE;                    last INSCOPE;
6030                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6031                    !!!cp ('t242');                    !!!cp ('t242');
6032                    last INSCOPE;                    last INSCOPE;
6033                  }                  }
6034                } # INSCOPE                } # INSCOPE
6035                unless (defined $i) {                unless (defined $i) {
6036                  !!!cp ('t243');                  !!!cp ('t243');
6037                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
6038                                    text => $token->{tag_name}, token => $token);
6039                  ## Ignore the token                  ## Ignore the token
6040                    !!!nack ('t243.1');
6041                  !!!next-token;                  !!!next-token;
6042                  redo B;                  next B;
6043                }                }
6044                                    
6045                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
# Line 4831  sub _tree_construction_main ($) { Line 6048  sub _tree_construction_main ($) {
6048                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
6049                                
6050                !!!next-token;                !!!next-token;
6051                redo B;                next B;
6052              } elsif ({              } elsif ({
6053                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
6054                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
# Line 4841  sub _tree_construction_main ($) { Line 6058  sub _tree_construction_main ($) {
6058                  my $i;                  my $i;
6059                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6060                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6061                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6062                      !!!cp ('t247');                      !!!cp ('t247');
6063                      $i = $_;                      $i = $_;
6064                      last INSCOPE;                      last INSCOPE;
6065                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6066                      !!!cp ('t248');                      !!!cp ('t248');
6067                      last INSCOPE;                      last INSCOPE;
6068                    }                    }
6069                  } # INSCOPE                  } # INSCOPE
6070                    unless (defined $i) {                    unless (defined $i) {
6071                      !!!cp ('t249');                      !!!cp ('t249');
6072                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                      !!!parse-error (type => 'unmatched end tag',
6073                                        text => $token->{tag_name}, token => $token);
6074                      ## Ignore the token                      ## Ignore the token
6075                        !!!nack ('t249.1');
6076                      !!!next-token;                      !!!next-token;
6077                      redo B;                      next B;
6078                    }                    }
6079                                    
6080                  ## As if </tr>                  ## As if </tr>
# Line 4865  sub _tree_construction_main ($) { Line 6082  sub _tree_construction_main ($) {
6082                  my $i;                  my $i;
6083                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6084                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6085                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
6086                      !!!cp ('t250');                      !!!cp ('t250');
6087                      $i = $_;                      $i = $_;
6088                      last INSCOPE;                      last INSCOPE;
6089                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6090                      !!!cp ('t251');                      !!!cp ('t251');
6091                      last INSCOPE;                      last INSCOPE;
6092                    }                    }
6093                  } # INSCOPE                  } # INSCOPE
6094                    unless (defined $i) {                    unless (defined $i) {
6095                      !!!cp ('t252');                      !!!cp ('t252');
6096                      !!!parse-error (type => 'unmatched end tag:tr', token => $token);                      !!!parse-error (type => 'unmatched end tag',
6097                                        text => 'tr', token => $token);
6098                      ## Ignore the token                      ## Ignore the token
6099                        !!!nack ('t252.1');
6100                      !!!next-token;                      !!!next-token;
6101                      redo B;                      next B;
6102                    }                    }
6103                                    
6104                  ## Clear back to table row context                  ## Clear back to table row context
6105                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6106                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6107                    !!!cp ('t253');                    !!!cp ('t253');
6108  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
6109                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4902  sub _tree_construction_main ($) { Line 6118  sub _tree_construction_main ($) {
6118                my $i;                my $i;
6119                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6120                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6121                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6122                    !!!cp ('t254');                    !!!cp ('t254');
6123                    $i = $_;                    $i = $_;
6124                    last INSCOPE;                    last INSCOPE;
6125                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6126                    !!!cp ('t255');                    !!!cp ('t255');
6127                    last INSCOPE;                    last INSCOPE;
6128                  }                  }
6129                } # INSCOPE                } # INSCOPE
6130                unless (defined $i) {                unless (defined $i) {
6131                  !!!cp ('t256');                  !!!cp ('t256');
6132                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
6133                                    text => $token->{tag_name}, token => $token);
6134                  ## Ignore the token                  ## Ignore the token
6135                    !!!nack ('t256.1');
6136                  !!!next-token;                  !!!next-token;
6137                  redo B;                  next B;
6138                }                }
6139    
6140                ## Clear back to table body context                ## Clear back to table body context
6141                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6142                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
6143                  !!!cp ('t257');                  !!!cp ('t257');
6144  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
6145                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
# Line 4932  sub _tree_construction_main ($) { Line 6147  sub _tree_construction_main ($) {
6147    
6148                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6149                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
6150                  !!!nack ('t257.1');
6151                !!!next-token;                !!!next-token;
6152                redo B;                next B;
6153              } elsif ({              } elsif ({
6154                        body => 1, caption => 1, col => 1, colgroup => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6155                        html => 1, td => 1, th => 1,                        html => 1, td => 1, th => 1,
6156                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6157                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6158                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6159                !!!cp ('t258');            !!!cp ('t258');
6160                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
6161                ## Ignore the token                            text => $token->{tag_name}, token => $token);
6162                !!!next-token;            ## Ignore the token
6163                redo B;            !!!nack ('t258.1');
6164               !!!next-token;
6165              next B;
6166          } else {          } else {
6167            !!!cp ('t259');            !!!cp ('t259');
6168            !!!parse-error (type => 'in table:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'in table:/',
6169                              text => $token->{tag_name}, token => $token);
6170    
6171            $insert = $insert_to_foster;            $insert = $insert_to_foster;
6172            #            #
6173          }          }
6174        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6175          unless ($self->{open_elements}->[-1]->[1] eq 'html' and          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6176                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
6177            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
6178            !!!cp ('t259.1');            !!!cp ('t259.1');
# Line 4970  sub _tree_construction_main ($) { Line 6189  sub _tree_construction_main ($) {
6189        }        }
6190      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6191            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6192              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6193                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6194                unless (length $token->{data}) {                unless (length $token->{data}) {
6195                  !!!cp ('t260');                  !!!cp ('t260');
6196                  !!!next-token;                  !!!next-token;
6197                  redo B;                  next B;
6198                }                }
6199              }              }
6200                            
# Line 4984  sub _tree_construction_main ($) { Line 6203  sub _tree_construction_main ($) {
6203            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
6204              if ($token->{tag_name} eq 'col') {              if ($token->{tag_name} eq 'col') {
6205                !!!cp ('t262');                !!!cp ('t262');
6206                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6207                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6208                  !!!ack ('t262.1');
6209                !!!next-token;                !!!next-token;
6210                redo B;                next B;
6211              } else {              } else {
6212                !!!cp ('t263');                !!!cp ('t263');
6213                #                #
6214              }              }
6215            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
6216              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
6217                if ($self->{open_elements}->[-1]->[1] eq 'html') {                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6218                  !!!cp ('t264');                  !!!cp ('t264');
6219                  !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);                  !!!parse-error (type => 'unmatched end tag',
6220                                    text => 'colgroup', token => $token);
6221                  ## Ignore the token                  ## Ignore the token
6222                  !!!next-token;                  !!!next-token;
6223                  redo B;                  next B;
6224                } else {                } else {
6225                  !!!cp ('t265');                  !!!cp ('t265');
6226                  pop @{$self->{open_elements}}; # colgroup                  pop @{$self->{open_elements}}; # colgroup
6227                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
6228                  !!!next-token;                  !!!next-token;
6229                  redo B;                              next B;            
6230                }                }
6231              } elsif ($token->{tag_name} eq 'col') {              } elsif ($token->{tag_name} eq 'col') {
6232                !!!cp ('t266');                !!!cp ('t266');
6233                !!!parse-error (type => 'unmatched end tag:col', token => $token);                !!!parse-error (type => 'unmatched end tag',
6234                                  text => 'col', token => $token);
6235                ## Ignore the token                ## Ignore the token
6236                !!!next-token;                !!!next-token;
6237                redo B;                next B;
6238              } else {              } else {
6239                !!!cp ('t267');                !!!cp ('t267');
6240                #                #
6241              }              }
6242        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6243          if ($self->{open_elements}->[-1]->[1] eq 'html' or          if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6244              @{$self->{open_elements}} == 1) { # redundant, maybe              @{$self->{open_elements}} == 1) { # redundant, maybe
6245            !!!cp ('t270.2');            !!!cp ('t270.2');
6246            ## Stop parsing.            ## Stop parsing.
# Line 5029  sub _tree_construction_main ($) { Line 6251  sub _tree_construction_main ($) {
6251            pop @{$self->{open_elements}}; # colgroup            pop @{$self->{open_elements}}; # colgroup
6252            $self->{insertion_mode} = IN_TABLE_IM;            $self->{insertion_mode} = IN_TABLE_IM;
6253            ## Reprocess.            ## Reprocess.
6254            redo B;            next B;
6255          }          }
6256        } else {        } else {
6257          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6258        }        }
6259    
6260            ## As if </colgroup>            ## As if </colgroup>
6261            if ($self->{open_elements}->[-1]->[1] eq 'html') {            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6262              !!!cp ('t269');              !!!cp ('t269');
6263  ## TODO: Wrong error type?  ## TODO: Wrong error type?
6264              !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);              !!!parse-error (type => 'unmatched end tag',
6265                                text => 'colgroup', token => $token);
6266              ## Ignore the token              ## Ignore the token
6267                !!!nack ('t269.1');
6268              !!!next-token;              !!!next-token;
6269              redo B;              next B;
6270            } else {            } else {
6271              !!!cp ('t270');              !!!cp ('t270');
6272              pop @{$self->{open_elements}}; # colgroup              pop @{$self->{open_elements}}; # colgroup
6273              $self->{insertion_mode} = IN_TABLE_IM;              $self->{insertion_mode} = IN_TABLE_IM;
6274                !!!ack-later;
6275              ## reprocess              ## reprocess
6276              redo B;              next B;
6277            }            }
6278      } elsif ($self->{insertion_mode} & SELECT_IMS) {      } elsif ($self->{insertion_mode} & SELECT_IMS) {
6279        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6280          !!!cp ('t271');          !!!cp ('t271');
6281          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6282          !!!next-token;          !!!next-token;
6283          redo B;          next B;
6284        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6285              if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
6286                if ($self->{open_elements}->[-1]->[1] eq 'option') {            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6287                  !!!cp ('t272');              !!!cp ('t272');
6288                  ## As if </option>              ## As if </option>
6289                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6290                } else {            } else {
6291                  !!!cp ('t273');              !!!cp ('t273');
6292                }            }
6293    
6294                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6295                !!!next-token;            !!!nack ('t273.1');
6296                redo B;            !!!next-token;
6297              } elsif ($token->{tag_name} eq 'optgroup') {            next B;
6298                if ($self->{open_elements}->[-1]->[1] eq 'option') {          } elsif ($token->{tag_name} eq 'optgroup') {
6299                  !!!cp ('t274');            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6300                  ## As if </option>              !!!cp ('t274');
6301                  pop @{$self->{open_elements}};              ## As if </option>
6302                } else {              pop @{$self->{open_elements}};
6303                  !!!cp ('t275');            } else {
6304                }              !!!cp ('t275');
6305              }
6306    
6307                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6308                  !!!cp ('t276');              !!!cp ('t276');
6309                  ## As if </optgroup>              ## As if </optgroup>
6310                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6311                } else {            } else {
6312                  !!!cp ('t277');              !!!cp ('t277');
6313                }            }
6314    
6315                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6316                !!!next-token;            !!!nack ('t277.1');
6317                redo B;            !!!next-token;
6318          } elsif ($token->{tag_name} eq 'select' or            next B;
6319                   $token->{tag_name} eq 'input' or          } elsif ({
6320                       select => 1, input => 1, textarea => 1,
6321                     }->{$token->{tag_name}} or
6322                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6323                    {                    {
6324                     caption => 1, table => 1,                     caption => 1, table => 1,
# Line 5098  sub _tree_construction_main ($) { Line 6326  sub _tree_construction_main ($) {
6326                     tr => 1, td => 1, th => 1,                     tr => 1, td => 1, th => 1,
6327                    }->{$token->{tag_name}})) {                    }->{$token->{tag_name}})) {
6328            ## TODO: The type below is not good - <select> is replaced by </select>            ## TODO: The type below is not good - <select> is replaced by </select>
6329            !!!parse-error (type => 'not closed:select', token => $token);            !!!parse-error (type => 'not closed', text => 'select',
6330                              token => $token);
6331            ## NOTE: As if the token were </select> (<select> case) or            ## NOTE: As if the token were </select> (<select> case) or
6332            ## as if there were </select> (otherwise).            ## as if there were </select> (otherwise).
6333                ## have an element in table scope            ## have an element in table scope
6334                my $i;            my $i;
6335                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6336                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6337                  if ($node->[1] eq 'select') {              if ($node->[1] & SELECT_EL) {
6338                    !!!cp ('t278');                !!!cp ('t278');
6339                    $i = $_;                $i = $_;
6340                    last INSCOPE;                last INSCOPE;
6341                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6342                            table => 1, html => 1,                !!!cp ('t279');
6343                           }->{$node->[1]}) {                last INSCOPE;
6344                    !!!cp ('t279');              }
6345                    last INSCOPE;            } # INSCOPE
6346                  }            unless (defined $i) {
6347                } # INSCOPE              !!!cp ('t280');
6348                unless (defined $i) {              !!!parse-error (type => 'unmatched end tag',
6349                  !!!cp ('t280');                              text => 'select', token => $token);
6350                  !!!parse-error (type => 'unmatched end tag:select', token => $token);              ## Ignore the token
6351                  ## Ignore the token              !!!nack ('t280.1');
6352                  !!!next-token;              !!!next-token;
6353                  redo B;              next B;
6354                }            }
6355                                
6356                !!!cp ('t281');            !!!cp ('t281');
6357                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6358    
6359                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6360    
6361            if ($token->{tag_name} eq 'select') {            if ($token->{tag_name} eq 'select') {
6362              !!!cp ('t281.2');              !!!nack ('t281.2');
6363              !!!next-token;              !!!next-token;
6364              redo B;              next B;
6365            } else {            } else {
6366              !!!cp ('t281.1');              !!!cp ('t281.1');
6367                !!!ack-later;
6368              ## Reprocess the token.              ## Reprocess the token.
6369              redo B;              next B;
6370            }            }
6371          } else {          } else {
6372            !!!cp ('t282');            !!!cp ('t282');
6373            !!!parse-error (type => 'in select:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'in select',
6374                              text => $token->{tag_name}, token => $token);
6375            ## Ignore the token            ## Ignore the token
6376              !!!nack ('t282.1');
6377            !!!next-token;            !!!next-token;
6378            redo B;            next B;
6379          }          }
6380        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6381              if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
6382                if ($self->{open_elements}->[-1]->[1] eq 'option' and            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
6383                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
6384                  !!!cp ('t283');              !!!cp ('t283');
6385                  ## As if </option>              ## As if </option>
6386                  splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
6387                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6388                  !!!cp ('t284');              !!!cp ('t284');
6389                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6390                } else {            } else {
6391                  !!!cp ('t285');              !!!cp ('t285');
6392                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
6393                  ## Ignore the token                              text => $token->{tag_name}, token => $token);
6394                }              ## Ignore the token
6395                !!!next-token;            }
6396                redo B;            !!!nack ('t285.1');
6397              } elsif ($token->{tag_name} eq 'option') {            !!!next-token;
6398                if ($self->{open_elements}->[-1]->[1] eq 'option') {            next B;
6399                  !!!cp ('t286');          } elsif ($token->{tag_name} eq 'option') {
6400                  pop @{$self->{open_elements}};            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6401                } else {              !!!cp ('t286');
6402                  !!!cp ('t287');              pop @{$self->{open_elements}};
6403                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            } else {
6404                  ## Ignore the token              !!!cp ('t287');
6405                }              !!!parse-error (type => 'unmatched end tag',
6406                !!!next-token;                              text => $token->{tag_name}, token => $token);
6407                redo B;              ## Ignore the token
6408              } elsif ($token->{tag_name} eq 'select') {            }
6409                ## have an element in table scope            !!!nack ('t287.1');
6410                my $i;            !!!next-token;
6411                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            next B;
6412                  my $node = $self->{open_elements}->[$_];          } elsif ($token->{tag_name} eq 'select') {
6413                  if ($node->[1] eq $token->{tag_name}) {            ## have an element in table scope
6414                    !!!cp ('t288');            my $i;
6415                    $i = $_;            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6416                    last INSCOPE;              my $node = $self->{open_elements}->[$_];
6417                  } elsif ({              if ($node->[1] & SELECT_EL) {
6418                            table => 1, html => 1,                !!!cp ('t288');
6419                           }->{$node->[1]}) {                $i = $_;
6420                    !!!cp ('t289');                last INSCOPE;
6421                    last INSCOPE;              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6422                  }                !!!cp ('t289');
6423                } # INSCOPE                last INSCOPE;
6424                unless (defined $i) {              }
6425                  !!!cp ('t290');            } # INSCOPE
6426                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            unless (defined $i) {
6427                  ## Ignore the token              !!!cp ('t290');
6428                  !!!next-token;              !!!parse-error (type => 'unmatched end tag',
6429                  redo B;                              text => $token->{tag_name}, token => $token);
6430                }              ## Ignore the token
6431                !!!nack ('t290.1');
6432                !!!next-token;
6433                next B;
6434              }
6435                                
6436                !!!cp ('t291');            !!!cp ('t291');
6437                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6438    
6439                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6440    
6441                !!!next-token;            !!!nack ('t291.1');
6442                redo B;            !!!next-token;
6443              next B;
6444          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6445                   {                   {
6446                    caption => 1, table => 1, tbody => 1,                    caption => 1, table => 1, tbody => 1,
6447                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6448                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
6449  ## TODO: The following is wrong?  ## TODO: The following is wrong?
6450                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
6451                              text => $token->{tag_name}, token => $token);
6452                                
6453                ## have an element in table scope            ## have an element in table scope
6454                my $i;            my $i;
6455                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6456                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6457                  if ($node->[1] eq $token->{tag_name}) {              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6458                    !!!cp ('t292');                !!!cp ('t292');
6459                    $i = $_;                $i = $_;
6460                    last INSCOPE;                last INSCOPE;
6461                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6462                            table => 1, html => 1,                !!!cp ('t293');
6463                           }->{$node->[1]}) {                last INSCOPE;
6464                    !!!cp ('t293');              }
6465                    last INSCOPE;            } # INSCOPE
6466                  }            unless (defined $i) {
6467                } # INSCOPE              !!!cp ('t294');
6468                unless (defined $i) {              ## Ignore the token
6469                  !!!cp ('t294');              !!!nack ('t294.1');
6470                  ## Ignore the token              !!!next-token;
6471                  !!!next-token;              next B;
6472                  redo B;            }
               }  
6473                                
6474                ## As if </select>            ## As if </select>
6475                ## have an element in table scope            ## have an element in table scope
6476                undef $i;            undef $i;
6477                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6478                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6479                  if ($node->[1] eq 'select') {              if ($node->[1] & SELECT_EL) {
6480                    !!!cp ('t295');                !!!cp ('t295');
6481                    $i = $_;                $i = $_;
6482                    last INSCOPE;                last INSCOPE;
6483                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6484  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
6485                    !!!cp ('t296');                !!!cp ('t296');
6486                    last INSCOPE;                last INSCOPE;
6487                  }              }
6488                } # INSCOPE            } # INSCOPE
6489                unless (defined $i) {            unless (defined $i) {
6490                  !!!cp ('t297');              !!!cp ('t297');
6491  ## TODO: The following error type is correct?  ## TODO: The following error type is correct?
6492                  !!!parse-error (type => 'unmatched end tag:select', token => $token);              !!!parse-error (type => 'unmatched end tag',
6493                  ## Ignore the </select> token                              text => 'select', token => $token);
6494                  !!!next-token; ## TODO: ok?              ## Ignore the </select> token
6495                  redo B;              !!!nack ('t297.1');
6496                }              !!!next-token; ## TODO: ok?
6497                next B;
6498              }
6499                                
6500                !!!cp ('t298');            !!!cp ('t298');
6501                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6502    
6503                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6504    
6505                ## reprocess            !!!ack-later;
6506                redo B;            ## reprocess
6507              next B;
6508          } else {          } else {
6509            !!!cp ('t299');            !!!cp ('t299');
6510            !!!parse-error (type => 'in select:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'in select:/',
6511                              text => $token->{tag_name}, token => $token);
6512            ## Ignore the token            ## Ignore the token
6513              !!!nack ('t299.3');
6514            !!!next-token;            !!!next-token;
6515            redo B;            next B;
6516          }          }
6517        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6518          unless ($self->{open_elements}->[-1]->[1] eq 'html' and          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6519                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
6520            !!!cp ('t299.1');            !!!cp ('t299.1');
6521            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 5290  sub _tree_construction_main ($) { Line 6530  sub _tree_construction_main ($) {
6530        }        }
6531      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6532        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6533          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6534            my $data = $1;            my $data = $1;
6535            ## As if in body            ## As if in body
6536            $reconstruct_active_formatting_elements->($insert_to_current);            $reconstruct_active_formatting_elements->($insert_to_current);
# Line 5300  sub _tree_construction_main ($) { Line 6540  sub _tree_construction_main ($) {
6540            unless (length $token->{data}) {            unless (length $token->{data}) {
6541              !!!cp ('t300');              !!!cp ('t300');
6542              !!!next-token;              !!!next-token;
6543              redo B;              next B;
6544            }            }
6545          }          }
6546                    
6547          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6548            !!!cp ('t301');            !!!cp ('t301');
6549            !!!parse-error (type => 'after html:#character', token => $token);            !!!parse-error (type => 'after html:#text', token => $token);
6550              #
           ## Reprocess in the "after body" insertion mode.  
6551          } else {          } else {
6552            !!!cp ('t302');            !!!cp ('t302');
6553              ## "after body" insertion mode
6554              !!!parse-error (type => 'after body:#text', token => $token);
6555              #
6556          }          }
           
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:#character', token => $token);  
6557    
6558          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6559          ## reprocess          ## reprocess
6560          redo B;          next B;
6561        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6562          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6563            !!!cp ('t303');            !!!cp ('t303');
6564            !!!parse-error (type => 'after html:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'after html',
6565                                        text => $token->{tag_name}, token => $token);
6566            ## Reprocess in the "after body" insertion mode.            #
6567          } else {          } else {
6568            !!!cp ('t304');            !!!cp ('t304');
6569              ## "after body" insertion mode
6570              !!!parse-error (type => 'after body',
6571                              text => $token->{tag_name}, token => $token);
6572              #
6573          }          }
6574    
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:'.$token->{tag_name}, token => $token);  
   
6575          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6576            !!!ack-later;
6577          ## reprocess          ## reprocess
6578          redo B;          next B;
6579        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6580          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6581            !!!cp ('t305');            !!!cp ('t305');
6582            !!!parse-error (type => 'after html:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'after html:/',
6583                              text => $token->{tag_name}, token => $token);
6584                        
6585            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6586            ## Reprocess in the "after body" insertion mode.            ## Reprocess.
6587              next B;
6588          } else {          } else {
6589            !!!cp ('t306');            !!!cp ('t306');
6590          }          }
# Line 5350  sub _tree_construction_main ($) { Line 6593  sub _tree_construction_main ($) {
6593          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
6594            if (defined $self->{inner_html_node}) {            if (defined $self->{inner_html_node}) {
6595              !!!cp ('t307');              !!!cp ('t307');
6596              !!!parse-error (type => 'unmatched end tag:html', token => $token);              !!!parse-error (type => 'unmatched end tag',
6597                                text => 'html', token => $token);
6598              ## Ignore the token              ## Ignore the token
6599              !!!next-token;              !!!next-token;
6600              redo B;              next B;
6601            } else {            } else {
6602              !!!cp ('t308');              !!!cp ('t308');
6603              $self->{insertion_mode} = AFTER_HTML_BODY_IM;              $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6604              !!!next-token;              !!!next-token;
6605              redo B;              next B;
6606            }            }
6607          } else {          } else {
6608            !!!cp ('t309');            !!!cp ('t309');
6609            !!!parse-error (type => 'after body:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'after body:/',
6610                              text => $token->{tag_name}, token => $token);
6611    
6612            $self->{insertion_mode} = IN_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6613            ## reprocess            ## reprocess
6614            redo B;            next B;
6615          }          }
6616        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6617          !!!cp ('t309.2');          !!!cp ('t309.2');
# Line 5377  sub _tree_construction_main ($) { Line 6622  sub _tree_construction_main ($) {
6622        }        }
6623      } elsif ($self->{insertion_mode} & FRAME_IMS) {      } elsif ($self->{insertion_mode} & FRAME_IMS) {
6624        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6625          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6626            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6627                        
6628            unless (length $token->{data}) {            unless (length $token->{data}) {
6629              !!!cp ('t310');              !!!cp ('t310');
6630              !!!next-token;              !!!next-token;
6631              redo B;              next B;
6632            }            }
6633          }          }
6634                    
6635          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6636            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6637              !!!cp ('t311');              !!!cp ('t311');
6638              !!!parse-error (type => 'in frameset:#character', token => $token);              !!!parse-error (type => 'in frameset:#text', token => $token);
6639            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6640              !!!cp ('t312');              !!!cp ('t312');
6641              !!!parse-error (type => 'after frameset:#character', token => $token);              !!!parse-error (type => 'after frameset:#text', token => $token);
6642            } else { # "after html frameset"            } else { # "after after frameset"
6643              !!!cp ('t313');              !!!cp ('t313');
6644              !!!parse-error (type => 'after html:#character', token => $token);              !!!parse-error (type => 'after html:#text', token => $token);
   
             $self->{insertion_mode} = AFTER_FRAMESET_IM;  
             ## Reprocess in the "after frameset" insertion mode.  
             !!!parse-error (type => 'after frameset:#character', token => $token);  
6645            }            }
6646                        
6647            ## Ignore the token.            ## Ignore the token.
# Line 5411  sub _tree_construction_main ($) { Line 6652  sub _tree_construction_main ($) {
6652              !!!cp ('t315');              !!!cp ('t315');
6653              !!!next-token;              !!!next-token;
6654            }            }
6655            redo B;            next B;
6656          }          }
6657                    
6658          die qq[$0: Character "$token->{data}"];          die qq[$0: Character "$token->{data}"];
6659        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
         if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {  
           !!!cp ('t316');  
           !!!parse-error (type => 'after html:'.$token->{tag_name}, token => $token);  
   
           $self->{insertion_mode} = AFTER_FRAMESET_IM;  
           ## Process in the "after frameset" insertion mode.  
         } else {  
           !!!cp ('t317');  
         }  
   
6660          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6661              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6662            !!!cp ('t318');            !!!cp ('t318');
6663            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6664              !!!nack ('t318.1');
6665            !!!next-token;            !!!next-token;
6666            redo B;            next B;
6667          } elsif ($token->{tag_name} eq 'frame' and          } elsif ($token->{tag_name} eq 'frame' and
6668                   $self->{insertion_mode} == IN_FRAMESET_IM) {                   $self->{insertion_mode} == IN_FRAMESET_IM) {
6669            !!!cp ('t319');            !!!cp ('t319');
6670            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6671            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
6672              !!!ack ('t319.1');
6673            !!!next-token;            !!!next-token;
6674            redo B;            next B;
6675          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
6676            !!!cp ('t320');            !!!cp ('t320');
6677            ## NOTE: As if in body.            ## NOTE: As if in head.
6678            $parse_rcdata->(CDATA_CONTENT_MODEL);            $parse_rcdata->(CDATA_CONTENT_MODEL);
6679            redo B;            next B;
6680    
6681              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6682              ## has no parse error.
6683          } else {          } else {
6684            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6685              !!!cp ('t321');              !!!cp ('t321');
6686              !!!parse-error (type => 'in frameset:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'in frameset',
6687            } else {                              text => $token->{tag_name}, token => $token);
6688              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6689              !!!cp ('t322');              !!!cp ('t322');
6690              !!!parse-error (type => 'after frameset:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'after frameset',
6691                                text => $token->{tag_name}, token => $token);
6692              } else { # "after after frameset"
6693                !!!cp ('t322.2');
6694                !!!parse-error (type => 'after after frameset',
6695                                text => $token->{tag_name}, token => $token);
6696            }            }
6697            ## Ignore the token            ## Ignore the token
6698              !!!nack ('t322.1');
6699            !!!next-token;            !!!next-token;
6700            redo B;            next B;
6701          }          }
6702        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
         if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {  
           !!!cp ('t323');  
           !!!parse-error (type => 'after html:/'.$token->{tag_name}, token => $token);  
   
           $self->{insertion_mode} = AFTER_FRAMESET_IM;  
           ## Process in the "after frameset" insertion mode.  
         } else {  
           !!!cp ('t324');  
         }  
   
6703          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6704              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6705            if ($self->{open_elements}->[-1]->[1] eq 'html' and            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6706                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
6707              !!!cp ('t325');              !!!cp ('t325');
6708              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
6709                                text => $token->{tag_name}, token => $token);
6710              ## Ignore the token              ## Ignore the token
6711              !!!next-token;              !!!next-token;
6712            } else {            } else {
# Line 5482  sub _tree_construction_main ($) { Line 6716  sub _tree_construction_main ($) {
6716            }            }
6717    
6718            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
6719                $self->{open_elements}->[-1]->[1] ne 'frameset') {                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
6720              !!!cp ('t327');              !!!cp ('t327');
6721              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
6722            } else {            } else {
6723              !!!cp ('t328');              !!!cp ('t328');
6724            }            }
6725            redo B;            next B;
6726          } elsif ($token->{tag_name} eq 'html' and          } elsif ($token->{tag_name} eq 'html' and
6727                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6728            !!!cp ('t329');            !!!cp ('t329');
6729            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6730            !!!next-token;            !!!next-token;
6731            redo B;            next B;
6732          } else {          } else {
6733            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6734              !!!cp ('t330');              !!!cp ('t330');
6735              !!!parse-error (type => 'in frameset:/'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'in frameset:/',
6736            } else {                              text => $token->{tag_name}, token => $token);
6737              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6738                !!!cp ('t330.1');
6739                !!!parse-error (type => 'after frameset:/',
6740                                text => $token->{tag_name}, token => $token);
6741              } else { # "after after html"
6742              !!!cp ('t331');              !!!cp ('t331');
6743              !!!parse-error (type => 'after frameset:/'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'after after frameset:/',
6744                                text => $token->{tag_name}, token => $token);
6745            }            }
6746            ## Ignore the token            ## Ignore the token
6747            !!!next-token;            !!!next-token;
6748            redo B;            next B;
6749          }          }
6750        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6751          unless ($self->{open_elements}->[-1]->[1] eq 'html' and          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6752                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
6753            !!!cp ('t331.1');            !!!cp ('t331.1');
6754            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 5533  sub _tree_construction_main ($) { Line 6773  sub _tree_construction_main ($) {
6773          !!!cp ('t332');          !!!cp ('t332');
6774          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6775          $script_start_tag->();          $script_start_tag->();
6776          redo B;          next B;
6777        } elsif ($token->{tag_name} eq 'style') {        } elsif ($token->{tag_name} eq 'style') {
6778          !!!cp ('t333');          !!!cp ('t333');
6779          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6780          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
6781          redo B;          next B;
6782        } elsif ({        } elsif ({
6783                  base => 1, link => 1,                  base => 1, link => 1,
6784                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6785          !!!cp ('t334');          !!!cp ('t334');
6786          ## NOTE: This is an "as if in head" code clone, only "-t" differs          ## NOTE: This is an "as if in head" code clone, only "-t" differs
6787          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6788          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
6789            !!!ack ('t334.1');
6790          !!!next-token;          !!!next-token;
6791          redo B;          next B;
6792        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
6793          ## NOTE: This is an "as if in head" code clone, only "-t" differs          ## NOTE: This is an "as if in head" code clone, only "-t" differs
6794          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6795          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
6796    
6797          unless ($self->{confident}) {          unless ($self->{confident}) {
6798            if ($token->{attributes}->{charset}) { ## TODO: And if supported            if ($token->{attributes}->{charset}) {
6799              !!!cp ('t335');              !!!cp ('t335');
6800                ## NOTE: Whether the encoding is supported or not is handled
6801                ## in the {change_encoding} callback.
6802              $self->{change_encoding}              $self->{change_encoding}
6803                  ->($self, $token->{attributes}->{charset}->{value});                  ->($self, $token->{attributes}->{charset}->{value}, $token);
6804                            
6805              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6806                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
6807                                       $token->{attributes}->{charset}                                       $token->{attributes}->{charset}
6808                                           ->{has_reference});                                           ->{has_reference});
6809            } elsif ($token->{attributes}->{content}) {            } elsif ($token->{attributes}->{content}) {
             ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
6810              if ($token->{attributes}->{content}->{value}              if ($token->{attributes}->{content}->{value}
6811                  =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6812                      [\x09-\x0D\x20]*=                      [\x09\x0A\x0C\x0D\x20]*=
6813                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                      [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6814                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                      ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6815                       /x) {
6816                !!!cp ('t336');                !!!cp ('t336');
6817                  ## NOTE: Whether the encoding is supported or not is handled
6818                  ## in the {change_encoding} callback.
6819                $self->{change_encoding}                $self->{change_encoding}
6820                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6821                $meta_el->[0]->get_attribute_node_ns (undef, 'content')                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6822                    ->set_user_data (manakai_has_reference =>                    ->set_user_data (manakai_has_reference =>
6823                                         $token->{attributes}->{content}                                         $token->{attributes}->{content}
# Line 5596  sub _tree_construction_main ($) { Line 6841  sub _tree_construction_main ($) {
6841            }            }
6842          }          }
6843    
6844            !!!ack ('t338.1');
6845          !!!next-token;          !!!next-token;
6846          redo B;          next B;
6847        } elsif ($token->{tag_name} eq 'title') {        } elsif ($token->{tag_name} eq 'title') {
6848          !!!cp ('t341');          !!!cp ('t341');
6849          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6850          $parse_rcdata->(RCDATA_CONTENT_MODEL);          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6851          redo B;          next B;
6852        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
6853          !!!parse-error (type => 'in body:body', token => $token);          !!!parse-error (type => 'in body', text => 'body', token => $token);
6854                                
6855          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
6856              $self->{open_elements}->[1]->[1] ne 'body') {              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
6857            !!!cp ('t342');            !!!cp ('t342');
6858            ## Ignore the token            ## Ignore the token
6859          } else {          } else {
# Line 5621  sub _tree_construction_main ($) { Line 6867  sub _tree_construction_main ($) {
6867              }              }
6868            }            }
6869          }          }
6870            !!!nack ('t343.1');
6871          !!!next-token;          !!!next-token;
6872          redo B;          next B;
6873        } elsif ({        } elsif ({
6874                  address => 1, blockquote => 1, center => 1, dir => 1,                  address => 1, blockquote => 1, center => 1, dir => 1,
6875                  div => 1, dl => 1, fieldset => 1,                  div => 1, dl => 1, fieldset => 1,
# Line 5637  sub _tree_construction_main ($) { Line 6884  sub _tree_construction_main ($) {
6884            !!!cp ('t350');            !!!cp ('t350');
6885            !!!parse-error (type => 'in form:form', token => $token);            !!!parse-error (type => 'in form:form', token => $token);
6886            ## Ignore the token            ## Ignore the token
6887              !!!nack ('t350.1');
6888            !!!next-token;            !!!next-token;
6889            redo B;            next B;
6890          }          }
6891    
6892          ## has a p element in scope          ## has a p element in scope
6893          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
6894            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
6895              !!!cp ('t344');              !!!cp ('t344');
6896              !!!back-token;              !!!back-token; # <form>
6897              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
6898              redo B;                        line => $token->{line}, column => $token->{column}};
6899            } elsif ({              next B;
6900                      applet => 1, table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
6901              !!!cp ('t345');              !!!cp ('t345');
6902              last INSCOPE;              last INSCOPE;
6903            }            }
6904          } # INSCOPE          } # INSCOPE
6905                        
6906          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6907          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
6908              !!!nack ('t346.1');
6909            !!!next-token;            !!!next-token;
6910            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6911              $token->{data} =~ s/^\x0A//;              $token->{data} =~ s/^\x0A//;
# Line 5675  sub _tree_construction_main ($) { Line 6922  sub _tree_construction_main ($) {
6922            !!!cp ('t347.1');            !!!cp ('t347.1');
6923            $self->{form_element} = $self->{open_elements}->[-1]->[0];            $self->{form_element} = $self->{open_elements}->[-1]->[0];
6924    
6925              !!!nack ('t347.2');
6926            !!!next-token;            !!!next-token;
6927          } elsif ($token->{tag_name} eq 'table') {          } elsif ($token->{tag_name} eq 'table') {
6928            !!!cp ('t382');            !!!cp ('t382');
# Line 5682  sub _tree_construction_main ($) { Line 6930  sub _tree_construction_main ($) {
6930                        
6931            $self->{insertion_mode} = IN_TABLE_IM;            $self->{insertion_mode} = IN_TABLE_IM;
6932    
6933              !!!nack ('t382.1');
6934            !!!next-token;            !!!next-token;
6935          } elsif ($token->{tag_name} eq 'hr') {          } elsif ($token->{tag_name} eq 'hr') {
6936            !!!cp ('t386');            !!!cp ('t386');
6937            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
6938                    
6939              !!!nack ('t386.1');
6940            !!!next-token;            !!!next-token;
6941          } else {          } else {
6942            !!!cp ('t347');            !!!nack ('t347.1');
6943            !!!next-token;            !!!next-token;
6944          }          }
6945          redo B;          next B;
6946        } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {        } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {
6947          ## has a p element in scope          ## has a p element in scope
6948          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
6949            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
6950              !!!cp ('t353');              !!!cp ('t353');
6951              !!!back-token;              !!!back-token; # <x>
6952              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
6953              redo B;                        line => $token->{line}, column => $token->{column}};
6954            } elsif ({              next B;
6955                      applet => 1, table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
6956              !!!cp ('t354');              !!!cp ('t354');
6957              last INSCOPE;              last INSCOPE;
6958            }            }
# Line 5718  sub _tree_construction_main ($) { Line 6966  sub _tree_construction_main ($) {
6966                            dd => {dt => 1, dd => 1}}->{$token->{tag_name}};                            dd => {dt => 1, dd => 1}}->{$token->{tag_name}};
6967          LI: {          LI: {
6968            ## Step 2            ## Step 2
6969            if ($li_or_dtdd->{$node->[1]}) {            if ($li_or_dtdd->{$node->[0]->manakai_local_name}) {
6970              if ($i != -1) {              if ($i != -1) {
6971                !!!cp ('t355');                !!!cp ('t355');
6972                !!!parse-error (type => 'end tag missing:'.                !!!parse-error (type => 'not closed',
6973                                $self->{open_elements}->[-1]->[1], token => $token);                                text => $self->{open_elements}->[-1]->[0]
6974                                      ->manakai_local_name,
6975                                  token => $token);
6976              } else {              } else {
6977                !!!cp ('t356');                !!!cp ('t356');
6978              }              }
# Line 5733  sub _tree_construction_main ($) { Line 6983  sub _tree_construction_main ($) {
6983            }            }
6984                        
6985            ## Step 3            ## Step 3
6986            if (not $formatting_category->{$node->[1]} and            if (not ($node->[1] & FORMATTING_EL) and
6987                #not $phrasing_category->{$node->[1]} and                #not $phrasing_category->{$node->[1]} and
6988                ($special_category->{$node->[1]} or                ($node->[1] & SPECIAL_EL or
6989                 $scoping_category->{$node->[1]}) and                 $node->[1] & SCOPING_EL) and
6990                $node->[1] ne 'address' and $node->[1] ne 'div') {                not ($node->[1] & ADDRESS_EL) and
6991                  not ($node->[1] & DIV_EL)) {
6992              !!!cp ('t358');              !!!cp ('t358');
6993              last LI;              last LI;
6994            }            }
# Line 5749  sub _tree_construction_main ($) { Line 7000  sub _tree_construction_main ($) {
7000            redo LI;            redo LI;
7001          } # LI          } # LI
7002                        
7003          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7004            !!!nack ('t359.1');
7005          !!!next-token;          !!!next-token;
7006          redo B;          next B;
7007        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
7008          ## has a p element in scope          ## has a p element in scope
7009          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7010            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7011              !!!cp ('t367');              !!!cp ('t367');
7012              !!!back-token;              !!!back-token; # <plaintext>
7013              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7014              redo B;                        line => $token->{line}, column => $token->{column}};
7015            } elsif ({              next B;
7016                      applet => 1, table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
7017              !!!cp ('t368');              !!!cp ('t368');
7018              last INSCOPE;              last INSCOPE;
7019            }            }
7020          } # INSCOPE          } # INSCOPE
7021                        
7022          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7023                        
7024          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7025                        
7026            !!!nack ('t368.1');
7027          !!!next-token;          !!!next-token;
7028          redo B;          next B;
7029        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
7030          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7031            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
7032            if ($node->[1] eq 'a') {            if ($node->[1] & A_EL) {
7033              !!!cp ('t371');              !!!cp ('t371');
7034              !!!parse-error (type => 'in a:a', token => $token);              !!!parse-error (type => 'in a:a', token => $token);
7035                            
7036              !!!back-token;              !!!back-token; # <a>
7037              $token = {type => END_TAG_TOKEN, tag_name => 'a'};              $token = {type => END_TAG_TOKEN, tag_name => 'a',
7038                          line => $token->{line}, column => $token->{column}};
7039              $formatting_end_tag->($token);              $formatting_end_tag->($token);
7040                            
7041              AFE2: for (reverse 0..$#$active_formatting_elements) {              AFE2: for (reverse 0..$#$active_formatting_elements) {
# Line 5809  sub _tree_construction_main ($) { Line 7061  sub _tree_construction_main ($) {
7061                        
7062          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7063    
7064          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7065          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7066    
7067            !!!nack ('t374.1');
7068          !!!next-token;          !!!next-token;
7069          redo B;          next B;
7070        } elsif ($token->{tag_name} eq 'nobr') {        } elsif ($token->{tag_name} eq 'nobr') {
7071          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7072    
7073          ## has a |nobr| element in scope          ## has a |nobr| element in scope
7074          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7075            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7076            if ($node->[1] eq 'nobr') {            if ($node->[1] & NOBR_EL) {
7077              !!!cp ('t376');              !!!cp ('t376');
7078              !!!parse-error (type => 'in nobr:nobr', token => $token);              !!!parse-error (type => 'in nobr:nobr', token => $token);
7079              !!!back-token;              !!!back-token; # <nobr>
7080              $token = {type => END_TAG_TOKEN, tag_name => 'nobr'};              $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7081              redo B;                        line => $token->{line}, column => $token->{column}};
7082            } elsif ({              next B;
7083                      applet => 1, table => 1, caption => 1, td => 1, th => 1,            } elsif ($node->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7084              !!!cp ('t377');              !!!cp ('t377');
7085              last INSCOPE;              last INSCOPE;
7086            }            }
7087          } # INSCOPE          } # INSCOPE
7088                    
7089          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7090          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7091                    
7092            !!!nack ('t377.1');
7093          !!!next-token;          !!!next-token;
7094          redo B;          next B;
7095        } elsif ($token->{tag_name} eq 'button') {        } elsif ($token->{tag_name} eq 'button') {
7096          ## has a button element in scope          ## has a button element in scope
7097          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7098            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7099            if ($node->[1] eq 'button') {            if ($node->[1] & BUTTON_EL) {
7100              !!!cp ('t378');              !!!cp ('t378');
7101              !!!parse-error (type => 'in button:button', token => $token);              !!!parse-error (type => 'in button:button', token => $token);
7102              !!!back-token;              !!!back-token; # <button>
7103              $token = {type => END_TAG_TOKEN, tag_name => 'button'};              $token = {type => END_TAG_TOKEN, tag_name => 'button',
7104              redo B;                        line => $token->{line}, column => $token->{column}};
7105            } elsif ({              next B;
7106                      applet => 1, table => 1, caption => 1, td => 1, th => 1,            } elsif ($node->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7107              !!!cp ('t379');              !!!cp ('t379');
7108              last INSCOPE;              last INSCOPE;
7109            }            }
# Line 5861  sub _tree_construction_main ($) { Line 7111  sub _tree_construction_main ($) {
7111                        
7112          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7113                        
7114          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7115    
7116          ## TODO: associate with $self->{form_element} if defined          ## TODO: associate with $self->{form_element} if defined
7117    
7118          push @$active_formatting_elements, ['#marker', ''];          push @$active_formatting_elements, ['#marker', ''];
7119    
7120            !!!nack ('t379.1');
7121          !!!next-token;          !!!next-token;
7122          redo B;          next B;
7123        } elsif ({        } elsif ({
7124                  xmp => 1,                  xmp => 1,
7125                  iframe => 1,                  iframe => 1,
7126                  noembed => 1,                  noembed => 1,
7127                  noframes => 1,                  noframes => 1, ## NOTE: This is an "as if in head" code clone.
7128                  noscript => 0, ## TODO: 1 if scripting is enabled                  noscript => 0, ## TODO: 1 if scripting is enabled
7129                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7130          if ($token->{tag_name} eq 'xmp') {          if ($token->{tag_name} eq 'xmp') {
# Line 5884  sub _tree_construction_main ($) { Line 7135  sub _tree_construction_main ($) {
7135          }          }
7136          ## NOTE: There is an "as if in body" code clone.          ## NOTE: There is an "as if in body" code clone.
7137          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
7138          redo B;          next B;
7139        } elsif ($token->{tag_name} eq 'isindex') {        } elsif ($token->{tag_name} eq 'isindex') {
7140          !!!parse-error (type => 'isindex', token => $token);          !!!parse-error (type => 'isindex', token => $token);
7141                    
7142          if (defined $self->{form_element}) {          if (defined $self->{form_element}) {
7143            !!!cp ('t389');            !!!cp ('t389');
7144            ## Ignore the token            ## Ignore the token
7145              !!!nack ('t389'); ## NOTE: Not acknowledged.
7146            !!!next-token;            !!!next-token;
7147            redo B;            next B;
7148          } else {          } else {
7149              !!!ack ('t391.1');
7150    
7151            my $at = $token->{attributes};            my $at = $token->{attributes};
7152            my $form_attrs;            my $form_attrs;
7153            $form_attrs->{action} = $at->{action} if $at->{action};            $form_attrs->{action} = $at->{action} if $at->{action};
# Line 5903  sub _tree_construction_main ($) { Line 7157  sub _tree_construction_main ($) {
7157            delete $at->{prompt};            delete $at->{prompt};
7158            my @tokens = (            my @tokens = (
7159                          {type => START_TAG_TOKEN, tag_name => 'form',                          {type => START_TAG_TOKEN, tag_name => 'form',
7160                           attributes => $form_attrs},                           attributes => $form_attrs,
7161                          {type => START_TAG_TOKEN, tag_name => 'hr'},                           line => $token->{line}, column => $token->{column}},
7162                          {type => START_TAG_TOKEN, tag_name => 'p'},                          {type => START_TAG_TOKEN, tag_name => 'hr',
7163                          {type => START_TAG_TOKEN, tag_name => 'label'},                           line => $token->{line}, column => $token->{column}},
7164                            {type => START_TAG_TOKEN, tag_name => 'p',
7165                             line => $token->{line}, column => $token->{column}},
7166                            {type => START_TAG_TOKEN, tag_name => 'label',
7167                             line => $token->{line}, column => $token->{column}},
7168                         );                         );
7169            if ($prompt_attr) {            if ($prompt_attr) {
7170              !!!cp ('t390');              !!!cp ('t390');
7171              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value}};              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7172                               #line => $token->{line}, column => $token->{column},
7173                              };
7174            } else {            } else {
7175              !!!cp ('t391');              !!!cp ('t391');
7176              push @tokens, {type => CHARACTER_TOKEN,              push @tokens, {type => CHARACTER_TOKEN,
7177                             data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD                             data => 'This is a searchable index. Insert your search keywords here: ',
7178                               #line => $token->{line}, column => $token->{column},
7179                              }; # SHOULD
7180              ## TODO: make this configurable              ## TODO: make this configurable
7181            }            }
7182            push @tokens,            push @tokens,
7183                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at},                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
7184                             line => $token->{line}, column => $token->{column}},
7185                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
7186                          {type => END_TAG_TOKEN, tag_name => 'label'},                          {type => END_TAG_TOKEN, tag_name => 'label',
7187                          {type => END_TAG_TOKEN, tag_name => 'p'},                           line => $token->{line}, column => $token->{column}},
7188                          {type => START_TAG_TOKEN, tag_name => 'hr'},                          {type => END_TAG_TOKEN, tag_name => 'p',
7189                          {type => END_TAG_TOKEN, tag_name => 'form'};                           line => $token->{line}, column => $token->{column}},
7190            $token = shift @tokens;                          {type => START_TAG_TOKEN, tag_name => 'hr',
7191                             line => $token->{line}, column => $token->{column}},
7192                            {type => END_TAG_TOKEN, tag_name => 'form',
7193                             line => $token->{line}, column => $token->{column}};
7194            !!!back-token (@tokens);            !!!back-token (@tokens);
7195            redo B;            !!!next-token;
7196              next B;
7197          }          }
7198        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
7199          my $tag_name = $token->{tag_name};          my $tag_name = $token->{tag_name};
7200          my $el;          my $el;
7201          !!!create-element ($el, $token->{tag_name}, $token->{attributes});          !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
7202                    
7203          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
7204          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
# Line 5940  sub _tree_construction_main ($) { Line 7207  sub _tree_construction_main ($) {
7207          $insert->($el);          $insert->($el);
7208                    
7209          my $text = '';          my $text = '';
7210            !!!nack ('t392.1');
7211          !!!next-token;          !!!next-token;
7212          if ($token->{type} == CHARACTER_TOKEN) {          if ($token->{type} == CHARACTER_TOKEN) {
7213            $token->{data} =~ s/^\x0A//;            $token->{data} =~ s/^\x0A//;
# Line 5970  sub _tree_construction_main ($) { Line 7238  sub _tree_construction_main ($) {
7238            ## Ignore the token            ## Ignore the token
7239          } else {          } else {
7240            !!!cp ('t398');            !!!cp ('t398');
7241            !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token);            !!!parse-error (type => 'in RCDATA:#eof', token => $token);
7242          }          }
7243          !!!next-token;          !!!next-token;
7244            next B;
7245          } elsif ($token->{tag_name} eq 'rt' or
7246                   $token->{tag_name} eq 'rp') {
7247            ## has a |ruby| element in scope
7248            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7249              my $node = $self->{open_elements}->[$_];
7250              if ($node->[1] & RUBY_EL) {
7251                !!!cp ('t398.1');
7252                ## generate implied end tags
7253                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7254                  !!!cp ('t398.2');
7255                  pop @{$self->{open_elements}};
7256                }
7257                unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7258                  !!!cp ('t398.3');
7259                  !!!parse-error (type => 'not closed',
7260                                  text => $self->{open_elements}->[-1]->[0]
7261                                      ->manakai_local_name,
7262                                  token => $token);
7263                  pop @{$self->{open_elements}}
7264                      while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
7265                }
7266                last INSCOPE;
7267              } elsif ($node->[1] & SCOPING_EL) {
7268                !!!cp ('t398.4');
7269                last INSCOPE;
7270              }
7271            } # INSCOPE
7272    
7273            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7274    
7275            !!!nack ('t398.5');
7276            !!!next-token;
7277          redo B;          redo B;
7278          } elsif ($token->{tag_name} eq 'math' or
7279                   $token->{tag_name} eq 'svg') {
7280            $reconstruct_active_formatting_elements->($insert_to_current);
7281    
7282            ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7283    
7284            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7285    
7286            ## "adjust foreign attributes" - done in insert-element-f
7287            
7288            !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7289            
7290            if ($self->{self_closing}) {
7291              pop @{$self->{open_elements}};
7292              !!!ack ('t398.1');
7293            } else {
7294              !!!cp ('t398.2');
7295              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7296              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7297              ## mode, "in body" (not "in foreign content") secondary insertion
7298              ## mode, maybe.
7299            }
7300    
7301            !!!next-token;
7302            next B;
7303        } elsif ({        } elsif ({
7304                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
7305                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1, option => 1, optgroup => 1,
# Line 5981  sub _tree_construction_main ($) { Line 7307  sub _tree_construction_main ($) {
7307                  thead => 1, tr => 1,                  thead => 1, tr => 1,
7308                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7309          !!!cp ('t401');          !!!cp ('t401');
7310          !!!parse-error (type => 'in body:'.$token->{tag_name}, token => $token);          !!!parse-error (type => 'in body',
7311                            text => $token->{tag_name}, token => $token);
7312          ## Ignore the token          ## Ignore the token
7313            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7314          !!!next-token;          !!!next-token;
7315          redo B;          next B;
7316                    
7317          ## ISSUE: An issue on HTML5 new elements in the spec.          ## ISSUE: An issue on HTML5 new elements in the spec.
7318        } else {        } else {
# Line 5999  sub _tree_construction_main ($) { Line 7327  sub _tree_construction_main ($) {
7327          ## NOTE: There is an "as if <br>" code clone.          ## NOTE: There is an "as if <br>" code clone.
7328          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7329                    
7330          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7331    
7332          if ({          if ({
7333               applet => 1, marquee => 1, object => 1,               applet => 1, marquee => 1, object => 1,
7334              }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
7335            !!!cp ('t380');            !!!cp ('t380');
7336            push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
7337              !!!nack ('t380.1');
7338          } elsif ({          } elsif ({
7339                    b => 1, big => 1, em => 1, font => 1, i => 1,                    b => 1, big => 1, em => 1, font => 1, i => 1,
7340                    s => 1, small => 1, strile => 1,                    s => 1, small => 1, strile => 1,
# Line 6013  sub _tree_construction_main ($) { Line 7342  sub _tree_construction_main ($) {
7342                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
7343            !!!cp ('t375');            !!!cp ('t375');
7344            push @$active_formatting_elements, $self->{open_elements}->[-1];            push @$active_formatting_elements, $self->{open_elements}->[-1];
7345              !!!nack ('t375.1');
7346          } elsif ($token->{tag_name} eq 'input') {          } elsif ($token->{tag_name} eq 'input') {
7347            !!!cp ('t388');            !!!cp ('t388');
7348            ## TODO: associate with $self->{form_element} if defined            ## TODO: associate with $self->{form_element} if defined
7349            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
7350              !!!ack ('t388.2');
7351          } elsif ({          } elsif ({
7352                    area => 1, basefont => 1, bgsound => 1, br => 1,                    area => 1, basefont => 1, bgsound => 1, br => 1,
7353                    embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                    embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,
# Line 6024  sub _tree_construction_main ($) { Line 7355  sub _tree_construction_main ($) {
7355                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
7356            !!!cp ('t388.1');            !!!cp ('t388.1');
7357            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
7358              !!!ack ('t388.3');
7359          } elsif ($token->{tag_name} eq 'select') {          } elsif ($token->{tag_name} eq 'select') {
7360            ## TODO: associate with $self->{form_element} if defined            ## TODO: associate with $self->{form_element} if defined
7361                    
# Line 6036  sub _tree_construction_main ($) { Line 7368  sub _tree_construction_main ($) {
7368              !!!cp ('t400.2');              !!!cp ('t400.2');
7369              $self->{insertion_mode} = IN_SELECT_IM;              $self->{insertion_mode} = IN_SELECT_IM;
7370            }            }
7371              !!!nack ('t400.3');
7372          } else {          } else {
7373            !!!cp ('t402');            !!!nack ('t402');
7374          }          }
7375                    
7376          !!!next-token;          !!!next-token;
7377          redo B;          next B;
7378        }        }
7379      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
7380        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
# Line 6049  sub _tree_construction_main ($) { Line 7382  sub _tree_construction_main ($) {
7382          my $i;          my $i;
7383          INSCOPE: {          INSCOPE: {
7384            for (reverse @{$self->{open_elements}}) {            for (reverse @{$self->{open_elements}}) {
7385              if ($_->[1] eq 'body') {              if ($_->[1] & BODY_EL) {
7386                !!!cp ('t405');                !!!cp ('t405');
7387                $i = $_;                $i = $_;
7388                last INSCOPE;                last INSCOPE;
7389              } elsif ({              } elsif ($_->[1] & SCOPING_EL) {
                       applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                       button => 1, marquee => 1, object => 1, html => 1,  
                      }->{$_->[1]}) {  
7390                !!!cp ('t405.1');                !!!cp ('t405.1');
7391                last;                last;
7392              }              }
7393            }            }
7394    
7395            !!!parse-error (type => 'start tag not allowed',            !!!parse-error (type => 'start tag not allowed',
7396                            value => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
7397            ## NOTE: Ignore the token.            ## NOTE: Ignore the token.
7398            !!!next-token;            !!!next-token;
7399            redo B;            next B;
7400          } # INSCOPE          } # INSCOPE
7401    
7402          for (@{$self->{open_elements}}) {          for (@{$self->{open_elements}}) {
7403            unless ({            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
                    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]}) {  
7404              !!!cp ('t403');              !!!cp ('t403');
7405              !!!parse-error (type => 'not closed:'.$_->[1], token => $token);              !!!parse-error (type => 'not closed',
7406                                text => $_->[0]->manakai_local_name,
7407                                token => $token);
7408              last;              last;
7409            } else {            } else {
7410              !!!cp ('t404');              !!!cp ('t404');
# Line 6085  sub _tree_construction_main ($) { Line 7413  sub _tree_construction_main ($) {
7413    
7414          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
7415          !!!next-token;          !!!next-token;
7416          redo B;          next B;
7417        } elsif ($token->{tag_name} eq 'html') {        } elsif ($token->{tag_name} eq 'html') {
7418          if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {          ## TODO: Update this code.  It seems that the code below is not
7419            ## up-to-date, though it has same effect as speced.
7420            if (@{$self->{open_elements}} > 1 and
7421                $self->{open_elements}->[1]->[1] & BODY_EL) {
7422            ## ISSUE: There is an issue in the spec.            ## ISSUE: There is an issue in the spec.
7423            if ($self->{open_elements}->[-1]->[1] ne 'body') {            unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7424              !!!cp ('t406');              !!!cp ('t406');
7425              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1], token => $token);              !!!parse-error (type => 'not closed',
7426                                text => $self->{open_elements}->[1]->[0]
7427                                    ->manakai_local_name,
7428                                token => $token);
7429            } else {            } else {
7430              !!!cp ('t407');              !!!cp ('t407');
7431            }            }
7432            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = AFTER_BODY_IM;
7433            ## reprocess            ## reprocess
7434            redo B;            next B;
7435          } else {          } else {
7436            !!!cp ('t408');            !!!cp ('t408');
7437            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7438                              text => $token->{tag_name}, token => $token);
7439            ## Ignore the token            ## Ignore the token
7440            !!!next-token;            !!!next-token;
7441            redo B;            next B;
7442          }          }
7443        } elsif ({        } elsif ({
7444                  address => 1, blockquote => 1, center => 1, dir => 1,                  address => 1, blockquote => 1, center => 1, dir => 1,
# Line 6116  sub _tree_construction_main ($) { Line 7451  sub _tree_construction_main ($) {
7451          my $i;          my $i;
7452          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7453            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7454            if ($node->[1] eq $token->{tag_name}) {            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7455              !!!cp ('t410');              !!!cp ('t410');
7456              $i = $_;              $i = $_;
7457              last INSCOPE;              last INSCOPE;
7458            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7459              !!!cp ('t411');              !!!cp ('t411');
7460              last INSCOPE;              last INSCOPE;
7461            }            }
# Line 6131  sub _tree_construction_main ($) { Line 7463  sub _tree_construction_main ($) {
7463    
7464          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7465            !!!cp ('t413');            !!!cp ('t413');
7466            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7467                              text => $token->{tag_name}, token => $token);
7468              ## NOTE: Ignore the token.
7469          } else {          } else {
7470            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7471            while ({            while ({
7472                      ## END_TAG_OPTIONAL_EL
7473                    dd => ($token->{tag_name} ne 'dd'),                    dd => ($token->{tag_name} ne 'dd'),
7474                    dt => ($token->{tag_name} ne 'dt'),                    dt => ($token->{tag_name} ne 'dt'),
7475                    li => ($token->{tag_name} ne 'li'),                    li => ($token->{tag_name} ne 'li'),
7476                    p => 1,                    p => 1,
7477                   }->{$self->{open_elements}->[-1]->[1]}) {                    rt => 1,
7478                      rp => 1,
7479                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7480              !!!cp ('t409');              !!!cp ('t409');
7481              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7482            }            }
7483    
7484            ## Step 2.            ## Step 2.
7485            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7486                      ne $token->{tag_name}) {
7487              !!!cp ('t412');              !!!cp ('t412');
7488              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);              !!!parse-error (type => 'not closed',
7489                                text => $self->{open_elements}->[-1]->[0]
7490                                    ->manakai_local_name,
7491                                token => $token);
7492            } else {            } else {
7493              !!!cp ('t414');              !!!cp ('t414');
7494            }            }
# Line 6162  sub _tree_construction_main ($) { Line 7503  sub _tree_construction_main ($) {
7503                }->{$token->{tag_name}};                }->{$token->{tag_name}};
7504          }          }
7505          !!!next-token;          !!!next-token;
7506          redo B;          next B;
7507        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
7508          undef $self->{form_element};          undef $self->{form_element};
7509    
# Line 6170  sub _tree_construction_main ($) { Line 7511  sub _tree_construction_main ($) {
7511          my $i;          my $i;
7512          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7513            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7514            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] & FORM_EL) {
7515              !!!cp ('t418');              !!!cp ('t418');
7516              $i = $_;              $i = $_;
7517              last INSCOPE;              last INSCOPE;
7518            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7519              !!!cp ('t419');              !!!cp ('t419');
7520              last INSCOPE;              last INSCOPE;
7521            }            }
# Line 6185  sub _tree_construction_main ($) { Line 7523  sub _tree_construction_main ($) {
7523    
7524          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7525            !!!cp ('t421');            !!!cp ('t421');
7526            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7527                              text => $token->{tag_name}, token => $token);
7528              ## NOTE: Ignore the token.
7529          } else {          } else {
7530            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7531            while ({            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                   dd => 1, dt => 1, li => 1, p => 1,  
                  }->{$self->{open_elements}->[-1]->[1]}) {  
7532              !!!cp ('t417');              !!!cp ('t417');
7533              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7534            }            }
7535                        
7536            ## Step 2.            ## Step 2.
7537            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7538                      ne $token->{tag_name}) {
7539              !!!cp ('t417.1');              !!!cp ('t417.1');
7540              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);              !!!parse-error (type => 'not closed',
7541                                text => $self->{open_elements}->[-1]->[0]
7542                                    ->manakai_local_name,
7543                                token => $token);
7544            } else {            } else {
7545              !!!cp ('t420');              !!!cp ('t420');
7546            }              }  
# Line 6208  sub _tree_construction_main ($) { Line 7550  sub _tree_construction_main ($) {
7550          }          }
7551    
7552          !!!next-token;          !!!next-token;
7553          redo B;          next B;
7554        } elsif ({        } elsif ({
7555                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7556                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
# Line 6216  sub _tree_construction_main ($) { Line 7558  sub _tree_construction_main ($) {
7558          my $i;          my $i;
7559          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7560            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7561            if ({            if ($node->[1] & HEADING_EL) {
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
7562              !!!cp ('t423');              !!!cp ('t423');
7563              $i = $_;              $i = $_;
7564              last INSCOPE;              last INSCOPE;
7565            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7566              !!!cp ('t424');              !!!cp ('t424');
7567              last INSCOPE;              last INSCOPE;
7568            }            }
# Line 6233  sub _tree_construction_main ($) { Line 7570  sub _tree_construction_main ($) {
7570    
7571          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7572            !!!cp ('t425.1');            !!!cp ('t425.1');
7573            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7574                              text => $token->{tag_name}, token => $token);
7575              ## NOTE: Ignore the token.
7576          } else {          } else {
7577            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7578            while ({            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                   dd => 1, dt => 1, li => 1, p => 1,  
                  }->{$self->{open_elements}->[-1]->[1]}) {  
7579              !!!cp ('t422');              !!!cp ('t422');
7580              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7581            }            }
7582                        
7583            ## Step 2.            ## Step 2.
7584            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7585                      ne $token->{tag_name}) {
7586              !!!cp ('t425');              !!!cp ('t425');
7587              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
7588                                text => $token->{tag_name}, token => $token);
7589            } else {            } else {
7590              !!!cp ('t426');              !!!cp ('t426');
7591            }            }
# Line 6256  sub _tree_construction_main ($) { Line 7595  sub _tree_construction_main ($) {
7595          }          }
7596                    
7597          !!!next-token;          !!!next-token;
7598          redo B;          next B;
7599        } elsif ($token->{tag_name} eq 'p') {        } elsif ($token->{tag_name} eq 'p') {
7600          ## has an element in scope          ## has an element in scope
7601          my $i;          my $i;
7602          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7603            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7604            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] & P_EL) {
7605              !!!cp ('t410.1');              !!!cp ('t410.1');
7606              $i = $_;              $i = $_;
7607              last INSCOPE;              last INSCOPE;
7608            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7609              !!!cp ('t411.1');              !!!cp ('t411.1');
7610              last INSCOPE;              last INSCOPE;
7611            }            }
7612          } # INSCOPE          } # INSCOPE
7613    
7614          if (defined $i) {          if (defined $i) {
7615            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7616                      ne $token->{tag_name}) {
7617              !!!cp ('t412.1');              !!!cp ('t412.1');
7618              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);              !!!parse-error (type => 'not closed',
7619                                text => $self->{open_elements}->[-1]->[0]
7620                                    ->manakai_local_name,
7621                                token => $token);
7622            } else {            } else {
7623              !!!cp ('t414.1');              !!!cp ('t414.1');
7624            }            }
# Line 6286  sub _tree_construction_main ($) { Line 7626  sub _tree_construction_main ($) {
7626            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
7627          } else {          } else {
7628            !!!cp ('t413.1');            !!!cp ('t413.1');
7629            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7630                              text => $token->{tag_name}, token => $token);
7631    
7632            !!!cp ('t415.1');            !!!cp ('t415.1');
7633            ## As if <p>, then reprocess the current token            ## As if <p>, then reprocess the current token
7634            my $el;            my $el;
7635            !!!create-element ($el, 'p');            !!!create-element ($el, $HTML_NS, 'p',, $token);
7636            $insert->($el);            $insert->($el);
7637            ## NOTE: Not inserted into |$self->{open_elements}|.            ## NOTE: Not inserted into |$self->{open_elements}|.
7638          }          }
7639    
7640          !!!next-token;          !!!next-token;
7641          redo B;          next B;
7642        } elsif ({        } elsif ({
7643                  a => 1,                  a => 1,
7644                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
# Line 6306  sub _tree_construction_main ($) { Line 7647  sub _tree_construction_main ($) {
7647                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7648          !!!cp ('t427');          !!!cp ('t427');
7649          $formatting_end_tag->($token);          $formatting_end_tag->($token);
7650          redo B;          next B;
7651        } elsif ($token->{tag_name} eq 'br') {        } elsif ($token->{tag_name} eq 'br') {
7652          !!!cp ('t428');          !!!cp ('t428');
7653          !!!parse-error (type => 'unmatched end tag:br', token => $token);          !!!parse-error (type => 'unmatched end tag',
7654                            text => 'br', token => $token);
7655    
7656          ## As if <br>          ## As if <br>
7657          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7658                    
7659          my $el;          my $el;
7660          !!!create-element ($el, 'br');          !!!create-element ($el, $HTML_NS, 'br',, $token);
7661          $insert->($el);          $insert->($el);
7662                    
7663          ## Ignore the token.          ## Ignore the token.
7664          !!!next-token;          !!!next-token;
7665          redo B;          next B;
7666        } elsif ({        } elsif ({
7667                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
7668                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1, option => 1, optgroup => 1,
# Line 6334  sub _tree_construction_main ($) { Line 7676  sub _tree_construction_main ($) {
7676                  noscript => 0, ## TODO: if scripting is enabled                  noscript => 0, ## TODO: if scripting is enabled
7677                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7678          !!!cp ('t429');          !!!cp ('t429');
7679          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);          !!!parse-error (type => 'unmatched end tag',
7680                            text => $token->{tag_name}, token => $token);
7681          ## Ignore the token          ## Ignore the token
7682          !!!next-token;          !!!next-token;
7683          redo B;          next B;
7684                    
7685          ## ISSUE: Issue on HTML5 new elements in spec          ## ISSUE: Issue on HTML5 new elements in spec
7686                    
# Line 6348  sub _tree_construction_main ($) { Line 7691  sub _tree_construction_main ($) {
7691    
7692          ## Step 2          ## Step 2
7693          S2: {          S2: {
7694            if ($node->[1] eq $token->{tag_name}) {            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7695              ## Step 1              ## Step 1
7696              ## generate implied end tags              ## generate implied end tags
7697              while ({              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                     dd => 1, dt => 1, li => 1, p => 1,  
                    }->{$self->{open_elements}->[-1]->[1]}) {  
7698                !!!cp ('t430');                !!!cp ('t430');
7699                ## ISSUE: Can this case be reached?                ## NOTE: |<ruby><rt></ruby>|.
7700                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7701                  ## which seems wrong.
7702                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
7703                  $node_i++;
7704              }              }
7705                    
7706              ## Step 2              ## Step 2
7707              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7708                        ne $token->{tag_name}) {
7709                !!!cp ('t431');                !!!cp ('t431');
7710                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
7711                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                !!!parse-error (type => 'not closed',
7712                                  text => $self->{open_elements}->[-1]->[0]
7713                                      ->manakai_local_name,
7714                                  token => $token);
7715              } else {              } else {
7716                !!!cp ('t432');                !!!cp ('t432');
7717              }              }
7718                            
7719              ## Step 3              ## Step 3
7720              splice @{$self->{open_elements}}, $node_i;              splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7721    
7722              !!!next-token;              !!!next-token;
7723              last S2;              last S2;
7724            } else {            } else {
7725              ## Step 3              ## Step 3
7726              if (not $formatting_category->{$node->[1]} and              if (not ($node->[1] & FORMATTING_EL) and
7727                  #not $phrasing_category->{$node->[1]} and                  #not $phrasing_category->{$node->[1]} and
7728                  ($special_category->{$node->[1]} or                  ($node->[1] & SPECIAL_EL or
7729                   $scoping_category->{$node->[1]})) {                   $node->[1] & SCOPING_EL)) {
7730                !!!cp ('t433');                !!!cp ('t433');
7731                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
7732                                  text => $token->{tag_name}, token => $token);
7733                ## Ignore the token                ## Ignore the token
7734                !!!next-token;                !!!next-token;
7735                last S2;                last S2;
# Line 6396  sub _tree_construction_main ($) { Line 7745  sub _tree_construction_main ($) {
7745            ## Step 5;            ## Step 5;
7746            redo S2;            redo S2;
7747          } # S2          } # S2
7748          redo B;          next B;
7749        }        }
7750      }      }
7751      redo B;      next B;
7752      } continue { # B
7753        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
7754          ## NOTE: The code below is executed in cases where it does not have
7755          ## to be, but it it is harmless even in those cases.
7756          ## has an element in scope
7757          INSCOPE: {
7758            for (reverse 0..$#{$self->{open_elements}}) {
7759              my $node = $self->{open_elements}->[$_];
7760              if ($node->[1] & FOREIGN_EL) {
7761                last INSCOPE;
7762              } elsif ($node->[1] & SCOPING_EL) {
7763                last;
7764              }
7765            }
7766            
7767            ## NOTE: No foreign element in scope.
7768            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
7769          } # INSCOPE
7770        }
7771    } # B    } # B
7772    
7773    ## Stop parsing # MUST    ## Stop parsing # MUST
# Line 6407  sub _tree_construction_main ($) { Line 7775  sub _tree_construction_main ($) {
7775    ## TODO: script stuffs    ## TODO: script stuffs
7776  } # _tree_construct_main  } # _tree_construct_main
7777    
7778  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
7779    my $class = shift;    my $class = shift;
7780    my $node = shift;    my $node = shift;
7781    my $s = \$_[0];    #my $s = \$_[0];
7782    my $onerror = $_[1];    my $onerror = $_[1];
7783      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
7784    
7785    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
7786    
# Line 6430  sub set_inner_html ($$$) { Line 7799  sub set_inner_html ($$$) {
7799      }      }
7800    
7801      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
7802      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
7803    } elsif ($nt == 1) {    } elsif ($nt == 1) {
7804      ## TODO: If non-html element      ## TODO: If non-html element
7805    
7806      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
7807    
7808    ## TODO: Support for $get_wrapper
7809    
7810      ## Step 1 # MUST      ## Step 1 # MUST
7811      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
7812      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 6445  sub set_inner_html ($$$) { Line 7816  sub set_inner_html ($$$) {
7816    
7817      ## Step 8 # MUST      ## Step 8 # MUST
7818      my $i = 0;      my $i = 0;
7819      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
7820      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
7821      $p->{set_next_char} = sub {      require Whatpm::Charset::DecodeHandle;
7822        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
7823        $input = $get_wrapper->($input);
7824        $p->{set_nc} = sub {
7825        my $self = shift;        my $self = shift;
7826    
7827        pop @{$self->{prev_char}};        my $char = '';
7828        unshift @{$self->{prev_char}}, $self->{next_char};        if (defined $self->{next_nc}) {
7829            $char = $self->{next_nc};
7830            delete $self->{next_nc};
7831            $self->{nc} = ord $char;
7832          } else {
7833            $self->{char_buffer} = '';
7834            $self->{char_buffer_pos} = 0;
7835            
7836            my $count = $input->manakai_read_until
7837                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
7838                 $self->{char_buffer_pos});
7839            if ($count) {
7840              $self->{line_prev} = $self->{line};
7841              $self->{column_prev} = $self->{column};
7842              $self->{column}++;
7843              $self->{nc}
7844                  = ord substr ($self->{char_buffer},
7845                                $self->{char_buffer_pos}++, 1);
7846              return;
7847            }
7848            
7849            if ($input->read ($char, 1)) {
7850              $self->{nc} = ord $char;
7851            } else {
7852              $self->{nc} = -1;
7853              return;
7854            }
7855          }
7856    
7857          ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
7858          $p->{column}++;
7859    
7860        $self->{next_char} = -1 and return if $i >= length $$s;        if ($self->{nc} == 0x000A) { # LF
7861        $self->{next_char} = ord substr $$s, $i++, 1;          $p->{line}++;
7862        $column++;          $p->{column} = 0;
   
       if ($self->{next_char} == 0x000A) { # LF  
         $line++;  
         $column = 0;  
7863          !!!cp ('i1');          !!!cp ('i1');
7864        } elsif ($self->{next_char} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
7865          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
7866          $self->{next_char} = 0x000A; # LF # MUST          my $next = '';
7867          $line++;          if ($input->read ($next, 1) and $next ne "\x0A") {
7868          $column = 0;            $self->{next_nc} = $next;
7869            }
7870            $self->{nc} = 0x000A; # LF # MUST
7871            $p->{line}++;
7872            $p->{column} = 0;
7873          !!!cp ('i2');          !!!cp ('i2');
7874        } elsif ($self->{next_char} > 0x10FFFF) {        } elsif ($self->{nc} == 0x0000) { # NULL
         $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST  
         !!!cp ('i3');  
       } elsif ($self->{next_char} == 0x0000) { # NULL  
7875          !!!cp ('i4');          !!!cp ('i4');
7876          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
7877          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
7878        }        }
7879      };      };
7880      $p->{prev_char} = [-1, -1, -1];  
7881      $p->{next_char} = -1;      $p->{read_until} = sub {
7882              #my ($scalar, $specials_range, $offset) = @_;
7883          return 0 if defined $p->{next_nc};
7884    
7885          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
7886          my $offset = $_[2] || 0;
7887          
7888          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
7889            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
7890            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
7891              substr ($_[0], $offset)
7892                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
7893              my $count = $+[0] - $-[0];
7894              if ($count) {
7895                $p->{column} += $count;
7896                $p->{char_buffer_pos} += $count;
7897                $p->{line_prev} = $p->{line};
7898                $p->{column_prev} = $p->{column} - 1;
7899                $p->{nc} = -1;
7900              }
7901              return $count;
7902            } else {
7903              return 0;
7904            }
7905          } else {
7906            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
7907            if ($count) {
7908              $p->{column} += $count;
7909              $p->{column_prev} += $count;
7910              $p->{nc} = -1;
7911            }
7912            return $count;
7913          }
7914        }; # $p->{read_until}
7915    
7916      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
7917        my (%opt) = @_;        my (%opt) = @_;
7918        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
7919          my $column = $opt{column};
7920          if (defined $opt{token} and defined $opt{token}->{line}) {
7921            $line = $opt{token}->{line};
7922            $column = $opt{token}->{column};
7923          }
7924          warn "Parse error ($opt{type}) at line $line column $column\n";
7925      };      };
7926      $p->{parse_error} = sub {      $p->{parse_error} = sub {
7927        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
7928      };      };
7929            
7930        my $char_onerror = sub {
7931          my (undef, $type, %opt) = @_;
7932          $ponerror->(layer => 'encode',
7933                      line => $p->{line}, column => $p->{column} + 1,
7934                      %opt, type => $type);
7935        }; # $char_onerror
7936        $input->onerror ($char_onerror);
7937    
7938      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
7939      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
7940    
# Line 6508  sub set_inner_html ($$$) { Line 7956  sub set_inner_html ($$$) {
7956          unless defined $p->{content_model};          unless defined $p->{content_model};
7957          ## ISSUE: What is "the name of the element"? local name?          ## ISSUE: What is "the name of the element"? local name?
7958    
7959      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
7960          ## TODO: Foreign element OK?
7961    
7962      ## Step 3      ## Step 3
7963      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
# Line 6518  sub set_inner_html ($$$) { Line 7967  sub set_inner_html ($$$) {
7967      $doc->append_child ($root);      $doc->append_child ($root);
7968    
7969      ## Step 5 # MUST      ## Step 5 # MUST
7970      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
7971    
7972      undef $p->{head_element};      undef $p->{head_element};
7973    
# Line 6564  sub set_inner_html ($$$) { Line 8013  sub set_inner_html ($$$) {
8013      ## ISSUE: mutation events?      ## ISSUE: mutation events?
8014    
8015      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
8016    
8017        delete $p->{parse_error}; # delete loop
8018    } else {    } else {
8019      die "$0: |set_inner_html| is not defined for node of type $nt";      die "$0: |set_inner_html| is not defined for node of type $nt";
8020    }    }

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24