/[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.116 by wakaba, Mon Mar 17 13:23:39 2008 UTC revision 1.193 by wakaba, Sat Oct 4 04:06:33 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      article => MISC_SPECIAL_EL,
145      aside => MISC_SPECIAL_EL,
146      b => FORMATTING_EL,
147      base => MISC_SPECIAL_EL,
148      basefont => MISC_SPECIAL_EL,
149      bgsound => MISC_SPECIAL_EL,
150      big => FORMATTING_EL,
151      blockquote => MISC_SPECIAL_EL,
152      body => BODY_EL,
153      br => MISC_SPECIAL_EL,
154      button => BUTTON_EL,
155      caption => CAPTION_EL,
156      center => MISC_SPECIAL_EL,
157      col => MISC_SPECIAL_EL,
158      colgroup => MISC_SPECIAL_EL,
159      command => MISC_SPECIAL_EL,
160      datagrid => MISC_SPECIAL_EL,
161      dd => DD_EL,
162      details => MISC_SPECIAL_EL,
163      dialog => MISC_SPECIAL_EL,
164      dir => MISC_SPECIAL_EL,
165      div => DIV_EL,
166      dl => MISC_SPECIAL_EL,
167      dt => DT_EL,
168      em => FORMATTING_EL,
169      embed => MISC_SPECIAL_EL,
170      eventsource => MISC_SPECIAL_EL,
171      fieldset => MISC_SPECIAL_EL,
172      figure => MISC_SPECIAL_EL,
173      font => FORMATTING_EL,
174      footer => MISC_SPECIAL_EL,
175      form => FORM_EL,
176      frame => MISC_SPECIAL_EL,
177      frameset => FRAMESET_EL,
178      h1 => HEADING_EL,
179      h2 => HEADING_EL,
180      h3 => HEADING_EL,
181      h4 => HEADING_EL,
182      h5 => HEADING_EL,
183      h6 => HEADING_EL,
184      head => MISC_SPECIAL_EL,
185      header => MISC_SPECIAL_EL,
186      hr => MISC_SPECIAL_EL,
187      html => HTML_EL,
188      i => FORMATTING_EL,
189      iframe => MISC_SPECIAL_EL,
190      img => MISC_SPECIAL_EL,
191      #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
192      input => MISC_SPECIAL_EL,
193      isindex => MISC_SPECIAL_EL,
194      li => LI_EL,
195      link => MISC_SPECIAL_EL,
196      listing => MISC_SPECIAL_EL,
197      marquee => MISC_SCOPING_EL,
198      menu => MISC_SPECIAL_EL,
199      meta => MISC_SPECIAL_EL,
200      nav => MISC_SPECIAL_EL,
201      nobr => NOBR_EL | FORMATTING_EL,
202      noembed => MISC_SPECIAL_EL,
203      noframes => MISC_SPECIAL_EL,
204      noscript => MISC_SPECIAL_EL,
205      object => MISC_SCOPING_EL,
206      ol => MISC_SPECIAL_EL,
207      optgroup => OPTGROUP_EL,
208      option => OPTION_EL,
209      p => P_EL,
210      param => MISC_SPECIAL_EL,
211      plaintext => MISC_SPECIAL_EL,
212      pre => MISC_SPECIAL_EL,
213      rp => RUBY_COMPONENT_EL,
214      rt => RUBY_COMPONENT_EL,
215      ruby => RUBY_EL,
216      s => FORMATTING_EL,
217      script => MISC_SPECIAL_EL,
218      select => SELECT_EL,
219      section => MISC_SPECIAL_EL,
220      small => FORMATTING_EL,
221      spacer => MISC_SPECIAL_EL,
222      strike => FORMATTING_EL,
223      strong => FORMATTING_EL,
224      style => MISC_SPECIAL_EL,
225      table => TABLE_EL,
226      tbody => TABLE_ROW_GROUP_EL,
227      td => TABLE_CELL_EL,
228      textarea => MISC_SPECIAL_EL,
229      tfoot => TABLE_ROW_GROUP_EL,
230      th => TABLE_CELL_EL,
231      thead => TABLE_ROW_GROUP_EL,
232      title => MISC_SPECIAL_EL,
233      tr => TABLE_ROW_EL,
234      tt => FORMATTING_EL,
235      u => FORMATTING_EL,
236      ul => MISC_SPECIAL_EL,
237      wbr => MISC_SPECIAL_EL,
238    };
239    
240    my $el_category_f = {
241      $MML_NS => {
242        'annotation-xml' => MML_AXML_EL,
243        mi => FOREIGN_FLOW_CONTENT_EL,
244        mo => FOREIGN_FLOW_CONTENT_EL,
245        mn => FOREIGN_FLOW_CONTENT_EL,
246        ms => FOREIGN_FLOW_CONTENT_EL,
247        mtext => FOREIGN_FLOW_CONTENT_EL,
248      },
249      $SVG_NS => {
250        foreignObject => FOREIGN_FLOW_CONTENT_EL,
251        desc => FOREIGN_FLOW_CONTENT_EL,
252        title => FOREIGN_FLOW_CONTENT_EL,
253      },
254      ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
255    };
256    
257    my $svg_attr_name = {
258      attributename => 'attributeName',
259      attributetype => 'attributeType',
260      basefrequency => 'baseFrequency',
261      baseprofile => 'baseProfile',
262      calcmode => 'calcMode',
263      clippathunits => 'clipPathUnits',
264      contentscripttype => 'contentScriptType',
265      contentstyletype => 'contentStyleType',
266      diffuseconstant => 'diffuseConstant',
267      edgemode => 'edgeMode',
268      externalresourcesrequired => 'externalResourcesRequired',
269      filterres => 'filterRes',
270      filterunits => 'filterUnits',
271      glyphref => 'glyphRef',
272      gradienttransform => 'gradientTransform',
273      gradientunits => 'gradientUnits',
274      kernelmatrix => 'kernelMatrix',
275      kernelunitlength => 'kernelUnitLength',
276      keypoints => 'keyPoints',
277      keysplines => 'keySplines',
278      keytimes => 'keyTimes',
279      lengthadjust => 'lengthAdjust',
280      limitingconeangle => 'limitingConeAngle',
281      markerheight => 'markerHeight',
282      markerunits => 'markerUnits',
283      markerwidth => 'markerWidth',
284      maskcontentunits => 'maskContentUnits',
285      maskunits => 'maskUnits',
286      numoctaves => 'numOctaves',
287      pathlength => 'pathLength',
288      patterncontentunits => 'patternContentUnits',
289      patterntransform => 'patternTransform',
290      patternunits => 'patternUnits',
291      pointsatx => 'pointsAtX',
292      pointsaty => 'pointsAtY',
293      pointsatz => 'pointsAtZ',
294      preservealpha => 'preserveAlpha',
295      preserveaspectratio => 'preserveAspectRatio',
296      primitiveunits => 'primitiveUnits',
297      refx => 'refX',
298      refy => 'refY',
299      repeatcount => 'repeatCount',
300      repeatdur => 'repeatDur',
301      requiredextensions => 'requiredExtensions',
302      requiredfeatures => 'requiredFeatures',
303      specularconstant => 'specularConstant',
304      specularexponent => 'specularExponent',
305      spreadmethod => 'spreadMethod',
306      startoffset => 'startOffset',
307      stddeviation => 'stdDeviation',
308      stitchtiles => 'stitchTiles',
309      surfacescale => 'surfaceScale',
310      systemlanguage => 'systemLanguage',
311      tablevalues => 'tableValues',
312      targetx => 'targetX',
313      targety => 'targetY',
314      textlength => 'textLength',
315      viewbox => 'viewBox',
316      viewtarget => 'viewTarget',
317      xchannelselector => 'xChannelSelector',
318      ychannelselector => 'yChannelSelector',
319      zoomandpan => 'zoomAndPan',
320    };
321    
322    my $foreign_attr_xname = {
323      'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
324      'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
325      'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
326      'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
327      'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
328      'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
329      'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
330      'xml:base' => [$XML_NS, ['xml', 'base']],
331      'xml:lang' => [$XML_NS, ['xml', 'lang']],
332      'xml:space' => [$XML_NS, ['xml', 'space']],
333      'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
334      'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
335  };  };
336    
337  my $c1_entity_char = {  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
338    
339    my $charref_map = {
340      0x0D => 0x000A,
341    0x80 => 0x20AC,    0x80 => 0x20AC,
342    0x81 => 0xFFFD,    0x81 => 0xFFFD,
343    0x82 => 0x201A,    0x82 => 0x201A,
# Line 59  my $c1_entity_char = { Line 370  my $c1_entity_char = {
370    0x9D => 0xFFFD,    0x9D => 0xFFFD,
371    0x9E => 0x017E,    0x9E => 0x017E,
372    0x9F => 0x0178,    0x9F => 0x0178,
373  }; # $c1_entity_char  }; # $charref_map
374    $charref_map->{$_} = 0xFFFD
375        for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
376            0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
377            0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
378            0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
379            0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
380            0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
381            0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
382    
383  my $special_category = {  ## TODO: Invoke the reset algorithm when a resettable element is
384    address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,  ## created (cf. HTML5 revision 2259).
   blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1,  
   dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1,  
   form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1,  
   h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1,  
   img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1,  
   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  
385    
386  sub parse_byte_string ($$$$;$) {  sub parse_byte_string ($$$$;$) {
387      my $self = shift;
388      my $charset_name = shift;
389      open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
390      return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
391    } # parse_byte_string
392    
393    sub parse_byte_stream ($$$$;$$) {
394      # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
395    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
396    my $charset = shift;    my $charset_name = shift;
397    my $bytes_s = ref $_[0] ? $_[0] : \($_[0]);    my $byte_stream = $_[0];
398    my $s;  
399        my $onerror = $_[2] || sub {
400    if (defined $charset) {      my (%opt) = @_;
401      require Encode; ## TODO: decode(utf8) don't delete BOM      warn "Parse error ($opt{type})\n";
402      $s = \ (Encode::decode ($charset, $$bytes_s));    };
403      $self->{input_encoding} = lc $charset; ## TODO: normalize name    $self->{parse_error} = $onerror; # updated later by parse_char_string
404      $self->{confident} = 1;  
405    } else {    my $get_wrapper = $_[3] || sub ($) {
406      ## TODO: Implement HTML5 detection algorithm      return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
407      };
408    
409      ## HTML5 encoding sniffing algorithm
410      require Message::Charset::Info;
411      my $charset;
412      my $buffer;
413      my ($char_stream, $e_status);
414    
415      SNIFFING: {
416        ## NOTE: By setting |allow_fallback| option true when the
417        ## |get_decode_handle| method is invoked, we ignore what the HTML5
418        ## spec requires, i.e. unsupported encoding should be ignored.
419          ## TODO: We should not do this unless the parser is invoked
420          ## in the conformance checking mode, in which this behavior
421          ## would be useful.
422    
423        ## Step 1
424        if (defined $charset_name) {
425          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
426              ## TODO: Is this ok?  Transfer protocol's parameter should be
427              ## interpreted in its semantics?
428    
429          ($char_stream, $e_status) = $charset->get_decode_handle
430              ($byte_stream, allow_error_reporting => 1,
431               allow_fallback => 1);
432          if ($char_stream) {
433            $self->{confident} = 1;
434            last SNIFFING;
435          } else {
436            !!!parse-error (type => 'charset:not supported',
437                            layer => 'encode',
438                            line => 1, column => 1,
439                            value => $charset_name,
440                            level => $self->{level}->{uncertain});
441          }
442        }
443    
444        ## Step 2
445        my $byte_buffer = '';
446        for (1..1024) {
447          my $char = $byte_stream->getc;
448          last unless defined $char;
449          $byte_buffer .= $char;
450        } ## TODO: timeout
451    
452        ## Step 3
453        if ($byte_buffer =~ /^\xFE\xFF/) {
454          $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
455          ($char_stream, $e_status) = $charset->get_decode_handle
456              ($byte_stream, allow_error_reporting => 1,
457               allow_fallback => 1, byte_buffer => \$byte_buffer);
458          $self->{confident} = 1;
459          last SNIFFING;
460        } elsif ($byte_buffer =~ /^\xFF\xFE/) {
461          $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
462          ($char_stream, $e_status) = $charset->get_decode_handle
463              ($byte_stream, allow_error_reporting => 1,
464               allow_fallback => 1, byte_buffer => \$byte_buffer);
465          $self->{confident} = 1;
466          last SNIFFING;
467        } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
468          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
469          ($char_stream, $e_status) = $charset->get_decode_handle
470              ($byte_stream, allow_error_reporting => 1,
471               allow_fallback => 1, byte_buffer => \$byte_buffer);
472          $self->{confident} = 1;
473          last SNIFFING;
474        }
475    
476        ## Step 4
477        ## TODO: <meta charset>
478    
479        ## Step 5
480        ## TODO: from history
481    
482        ## Step 6
483      require Whatpm::Charset::UniversalCharDet;      require Whatpm::Charset::UniversalCharDet;
484      $charset = Whatpm::Charset::UniversalCharDet->detect_byte_string      $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
485          (substr ($$bytes_s, 0, 1024));          ($byte_buffer);
486      $charset ||= 'windows-1252';      if (defined $charset_name) {
487      $s = \ (Encode::decode ($charset, $$bytes_s));        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
488      $self->{input_encoding} = $charset;  
489          ## ISSUE: Unsupported encoding is not ignored according to the spec.
490          require Whatpm::Charset::DecodeHandle;
491          $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
492              ($byte_stream);
493          ($char_stream, $e_status) = $charset->get_decode_handle
494              ($buffer, allow_error_reporting => 1,
495               allow_fallback => 1, byte_buffer => \$byte_buffer);
496          if ($char_stream) {
497            $buffer->{buffer} = $byte_buffer;
498            !!!parse-error (type => 'sniffing:chardet',
499                            text => $charset_name,
500                            level => $self->{level}->{info},
501                            layer => 'encode',
502                            line => 1, column => 1);
503            $self->{confident} = 0;
504            last SNIFFING;
505          }
506        }
507    
508        ## Step 7: default
509        ## TODO: Make this configurable.
510        $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
511            ## NOTE: We choose |windows-1252| here, since |utf-8| should be
512            ## detectable in the step 6.
513        require Whatpm::Charset::DecodeHandle;
514        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
515            ($byte_stream);
516        ($char_stream, $e_status)
517            = $charset->get_decode_handle ($buffer,
518                                           allow_error_reporting => 1,
519                                           allow_fallback => 1,
520                                           byte_buffer => \$byte_buffer);
521        $buffer->{buffer} = $byte_buffer;
522        !!!parse-error (type => 'sniffing:default',
523                        text => 'windows-1252',
524                        level => $self->{level}->{info},
525                        line => 1, column => 1,
526                        layer => 'encode');
527      $self->{confident} = 0;      $self->{confident} = 0;
528      } # SNIFFING
529    
530      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
531        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
532        !!!parse-error (type => 'chardecode:fallback',
533                        #text => $self->{input_encoding},
534                        level => $self->{level}->{uncertain},
535                        line => 1, column => 1,
536                        layer => 'encode');
537      } elsif (not ($e_status &
538                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
539        $self->{input_encoding} = $charset->get_iana_name;
540        !!!parse-error (type => 'chardecode:no error',
541                        text => $self->{input_encoding},
542                        level => $self->{level}->{uncertain},
543                        line => 1, column => 1,
544                        layer => 'encode');
545      } else {
546        $self->{input_encoding} = $charset->get_iana_name;
547    }    }
548    
549    $self->{change_encoding} = sub {    $self->{change_encoding} = sub {
550      my $self = shift;      my $self = shift;
551      my $charset = lc shift;      $charset_name = shift;
552      my $token = shift;      my $token = shift;
     ## TODO: if $charset is supported  
     ## TODO: normalize charset name  
553    
554      ## "Change the encoding" algorithm:      $charset = Message::Charset::Info->get_by_html_name ($charset_name);
555        ($char_stream, $e_status) = $charset->get_decode_handle
556            ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
557             byte_buffer => \ $buffer->{buffer});
558        
559        if ($char_stream) { # if supported
560          ## "Change the encoding" algorithm:
561    
562      ## Step 1            ## Step 1    
563      if ($charset eq 'utf-16') { ## ISSUE: UTF-16BE -> UTF-8? UTF-16LE -> UTF-8?        if ($charset->{category} &
564        $charset = 'utf-8';            Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
565      }          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
566            ($char_stream, $e_status) = $charset->get_decode_handle
567                ($byte_stream,
568                 byte_buffer => \ $buffer->{buffer});
569          }
570          $charset_name = $charset->get_iana_name;
571          
572          ## Step 2
573          if (defined $self->{input_encoding} and
574              $self->{input_encoding} eq $charset_name) {
575            !!!parse-error (type => 'charset label:matching',
576                            text => $charset_name,
577                            level => $self->{level}->{info});
578            $self->{confident} = 1;
579            return;
580          }
581    
582      ## Step 2        !!!parse-error (type => 'charset label detected',
583      if (defined $self->{input_encoding} and                        text => $self->{input_encoding},
584          $self->{input_encoding} eq $charset) {                        value => $charset_name,
585        $self->{confident} = 1;                        level => $self->{level}->{warn},
586        return;                        token => $token);
587          
588          ## Step 3
589          # if (can) {
590            ## change the encoding on the fly.
591            #$self->{confident} = 1;
592            #return;
593          # }
594          
595          ## Step 4
596          throw Whatpm::HTML::RestartParser ();
597      }      }
598      }; # $self->{change_encoding}
599    
600      !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}.    my $char_onerror = sub {
601          ':'.$charset, level => 'w', token => $token);      my (undef, $type, %opt) = @_;
602        !!!parse-error (layer => 'encode',
603      ## Step 3                      line => $self->{line}, column => $self->{column} + 1,
604      # if (can) {                      %opt, type => $type);
605        ## change the encoding on the fly.      if ($opt{octets}) {
606        #$self->{confident} = 1;        ${$opt{octets}} = "\x{FFFD}"; # relacement character
607        #return;      }
608      # }    };
609    
610      ## Step 4    my $wrapped_char_stream = $get_wrapper->($char_stream);
611      throw Whatpm::HTML::RestartParser (charset => $charset);    $wrapped_char_stream->onerror ($char_onerror);
   }; # $self->{change_encoding}  
612    
613    my @args = @_; shift @args; # $s    my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
614    my $return;    my $return;
615    try {    try {
616      $return = $self->parse_char_string ($s, @args);        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
617    } catch Whatpm::HTML::RestartParser with {    } catch Whatpm::HTML::RestartParser with {
618      my $charset = shift->{charset};      ## NOTE: Invoked after {change_encoding}.
619      $s = \ (Encode::decode ($charset, $$bytes_s));      
620      $self->{input_encoding} = $charset; ## TODO: normalize      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
621          $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
622          !!!parse-error (type => 'chardecode:fallback',
623                          level => $self->{level}->{uncertain},
624                          #text => $self->{input_encoding},
625                          line => 1, column => 1,
626                          layer => 'encode');
627        } elsif (not ($e_status &
628                      Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
629          $self->{input_encoding} = $charset->get_iana_name;
630          !!!parse-error (type => 'chardecode:no error',
631                          text => $self->{input_encoding},
632                          level => $self->{level}->{uncertain},
633                          line => 1, column => 1,
634                          layer => 'encode');
635        } else {
636          $self->{input_encoding} = $charset->get_iana_name;
637        }
638      $self->{confident} = 1;      $self->{confident} = 1;
639      $return = $self->parse_char_string ($s, @args);  
640        $wrapped_char_stream = $get_wrapper->($char_stream);
641        $wrapped_char_stream->onerror ($char_onerror);
642    
643        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
644    };    };
645    return $return;    return $return;
646  } # parse_byte_string  } # parse_byte_stream
647    
648  ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM  ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
649  ## 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 163  sub parse_byte_string ($$$$;$) { Line 654  sub parse_byte_string ($$$$;$) {
654  ## 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
655  ## strip the BOM and never strip any ZWNBSP.  ## strip the BOM and never strip any ZWNBSP.
656    
657  *parse_char_string = \&parse_string;  sub parse_char_string ($$$;$$) {
658      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
659      my $self = shift;
660      my $s = ref $_[0] ? $_[0] : \($_[0]);
661      require Whatpm::Charset::DecodeHandle;
662      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
663      return $self->parse_char_stream ($input, @_[1..$#_]);
664    } # parse_char_string
665    *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
666    
667  sub parse_string ($$$;$) {  sub parse_char_stream ($$$;$$) {
668    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
669    my $s = ref $_[0] ? $_[0] : \($_[0]);    my $input = $_[0];
670    $self->{document} = $_[1];    $self->{document} = $_[1];
671    @{$self->{document}->child_nodes} = ();    @{$self->{document}->child_nodes} = ();
672    
# Line 176  sub parse_string ($$$;$) { Line 675  sub parse_string ($$$;$) {
675    $self->{confident} = 1 unless exists $self->{confident};    $self->{confident} = 1 unless exists $self->{confident};
676    $self->{document}->input_encoding ($self->{input_encoding})    $self->{document}->input_encoding ($self->{input_encoding})
677        if defined $self->{input_encoding};        if defined $self->{input_encoding};
678    ## TODO: |{input_encoding}| is needless?
679    
   my $i = 0;  
680    $self->{line_prev} = $self->{line} = 1;    $self->{line_prev} = $self->{line} = 1;
681    $self->{column_prev} = $self->{column} = 0;    $self->{column_prev} = -1;
682    $self->{set_next_char} = sub {    $self->{column} = 0;
683      $self->{set_nc} = sub {
684      my $self = shift;      my $self = shift;
685    
686      pop @{$self->{prev_char}};      my $char = '';
687      unshift @{$self->{prev_char}}, $self->{next_char};      if (defined $self->{next_nc}) {
688          $char = $self->{next_nc};
689          delete $self->{next_nc};
690          $self->{nc} = ord $char;
691        } else {
692          $self->{char_buffer} = '';
693          $self->{char_buffer_pos} = 0;
694    
695      $self->{next_char} = -1 and return if $i >= length $$s;        my $count = $input->manakai_read_until
696      $self->{next_char} = ord substr $$s, $i++, 1;           ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
697          if ($count) {
698            $self->{line_prev} = $self->{line};
699            $self->{column_prev} = $self->{column};
700            $self->{column}++;
701            $self->{nc}
702                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
703            return;
704          }
705    
706          if ($input->read ($char, 1)) {
707            $self->{nc} = ord $char;
708          } else {
709            $self->{nc} = -1;
710            return;
711          }
712        }
713    
714      ($self->{line_prev}, $self->{column_prev})      ($self->{line_prev}, $self->{column_prev})
715          = ($self->{line}, $self->{column});          = ($self->{line}, $self->{column});
716      $self->{column}++;      $self->{column}++;
717            
718      if ($self->{next_char} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
719          !!!cp ('j1');
720        $self->{line}++;        $self->{line}++;
721        $self->{column} = 0;        $self->{column} = 0;
722      } elsif ($self->{next_char} == 0x000D) { # CR      } elsif ($self->{nc} == 0x000D) { # CR
723        $i++ if substr ($$s, $i, 1) eq "\x0A";        !!!cp ('j2');
724        $self->{next_char} = 0x000A; # LF # MUST  ## TODO: support for abort/streaming
725          my $next = '';
726          if ($input->read ($next, 1) and $next ne "\x0A") {
727            $self->{next_nc} = $next;
728          }
729          $self->{nc} = 0x000A; # LF # MUST
730        $self->{line}++;        $self->{line}++;
731        $self->{column} = 0;        $self->{column} = 0;
732      } elsif ($self->{next_char} > 0x10FFFF) {      } elsif ($self->{nc} == 0x0000) { # NULL
733        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        !!!cp ('j4');
     } elsif ($self->{next_char} == 0x0000) { # NULL  
734        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
735        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
736      }      }
737    };    };
738    $self->{prev_char} = [-1, -1, -1];  
739    $self->{next_char} = -1;    $self->{read_until} = sub {
740        #my ($scalar, $specials_range, $offset) = @_;
741        return 0 if defined $self->{next_nc};
742    
743        my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
744        my $offset = $_[2] || 0;
745    
746        if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
747          pos ($self->{char_buffer}) = $self->{char_buffer_pos};
748          if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
749            substr ($_[0], $offset)
750                = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
751            my $count = $+[0] - $-[0];
752            if ($count) {
753              $self->{column} += $count;
754              $self->{char_buffer_pos} += $count;
755              $self->{line_prev} = $self->{line};
756              $self->{column_prev} = $self->{column} - 1;
757              $self->{nc} = -1;
758            }
759            return $count;
760          } else {
761            return 0;
762          }
763        } else {
764          my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
765          if ($count) {
766            $self->{column} += $count;
767            $self->{line_prev} = $self->{line};
768            $self->{column_prev} = $self->{column} - 1;
769            $self->{nc} = -1;
770          }
771          return $count;
772        }
773      }; # $self->{read_until}
774    
775    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
776      my (%opt) = @_;      my (%opt) = @_;
# Line 221  sub parse_string ($$$;$) { Line 782  sub parse_string ($$$;$) {
782      $onerror->(line => $self->{line}, column => $self->{column}, @_);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
783    };    };
784    
785      my $char_onerror = sub {
786        my (undef, $type, %opt) = @_;
787        !!!parse-error (layer => 'encode',
788                        line => $self->{line}, column => $self->{column} + 1,
789                        %opt, type => $type);
790      }; # $char_onerror
791    
792      if ($_[3]) {
793        $input = $_[3]->($input);
794        $input->onerror ($char_onerror);
795      } else {
796        $input->onerror ($char_onerror) unless defined $input->onerror;
797      }
798    
799    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
800    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
801    $self->_construct_tree;    $self->_construct_tree;
# Line 229  sub parse_string ($$$;$) { Line 804  sub parse_string ($$$;$) {
804    delete $self->{parse_error}; # remove loop    delete $self->{parse_error}; # remove loop
805    
806    return $self->{document};    return $self->{document};
807  } # parse_string  } # parse_char_stream
808    
809  sub new ($) {  sub new ($) {
810    my $class = shift;    my $class = shift;
811    my $self = bless {}, $class;    my $self = bless {
812    $self->{set_next_char} = sub {      level => {must => 'm',
813      $self->{next_char} = -1;                should => 's',
814                  warn => 'w',
815                  info => 'i',
816                  uncertain => 'u'},
817      }, $class;
818      $self->{set_nc} = sub {
819        $self->{nc} = -1;
820    };    };
821    $self->{parse_error} = sub {    $self->{parse_error} = sub {
822      #      #
# Line 262  sub RCDATA_CONTENT_MODEL () { CM_ENTITY Line 843  sub RCDATA_CONTENT_MODEL () { CM_ENTITY
843  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
844    
845  sub DATA_STATE () { 0 }  sub DATA_STATE () { 0 }
846  sub ENTITY_DATA_STATE () { 1 }  #sub ENTITY_DATA_STATE () { 1 }
847  sub TAG_OPEN_STATE () { 2 }  sub TAG_OPEN_STATE () { 2 }
848  sub CLOSE_TAG_OPEN_STATE () { 3 }  sub CLOSE_TAG_OPEN_STATE () { 3 }
849  sub TAG_NAME_STATE () { 4 }  sub TAG_NAME_STATE () { 4 }
# Line 273  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 Line 854  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8
854  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
855  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
856  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
857  sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }  #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
858  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
859  sub COMMENT_START_STATE () { 14 }  sub COMMENT_START_STATE () { 14 }
860  sub COMMENT_START_DASH_STATE () { 15 }  sub COMMENT_START_DASH_STATE () { 15 }
# Line 295  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO Line 876  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO
876  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
877  sub BOGUS_DOCTYPE_STATE () { 32 }  sub BOGUS_DOCTYPE_STATE () { 32 }
878  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
879    sub SELF_CLOSING_START_TAG_STATE () { 34 }
880    sub CDATA_SECTION_STATE () { 35 }
881    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
882    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
883    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
884    sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
885    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
886    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
887    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
888    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
889    ## NOTE: "Entity data state", "entity in attribute value state", and
890    ## "consume a character reference" algorithm are jointly implemented
891    ## using the following six states:
892    sub ENTITY_STATE () { 44 }
893    sub ENTITY_HASH_STATE () { 45 }
894    sub NCR_NUM_STATE () { 46 }
895    sub HEXREF_X_STATE () { 47 }
896    sub HEXREF_HEX_STATE () { 48 }
897    sub ENTITY_NAME_STATE () { 49 }
898    sub PCDATA_STATE () { 50 } # "data state" in the spec
899    
900  sub DOCTYPE_TOKEN () { 1 }  sub DOCTYPE_TOKEN () { 1 }
901  sub COMMENT_TOKEN () { 2 }  sub COMMENT_TOKEN () { 2 }
# Line 312  sub ROW_IMS ()        { 0b10000000 } Line 913  sub ROW_IMS ()        { 0b10000000 }
913  sub BODY_AFTER_IMS () { 0b100000000 }  sub BODY_AFTER_IMS () { 0b100000000 }
914  sub FRAME_IMS ()      { 0b1000000000 }  sub FRAME_IMS ()      { 0b1000000000 }
915  sub SELECT_IMS ()     { 0b10000000000 }  sub SELECT_IMS ()     { 0b10000000000 }
916    sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
917        ## NOTE: "in foreign content" insertion mode is special; it is combined
918        ## with the secondary insertion mode.  In this parser, they are stored
919        ## together in the bit-or'ed form.
920    
921  ## NOTE: "initial" and "before html" insertion modes have no constants.  ## NOTE: "initial" and "before html" insertion modes have no constants.
922    
# Line 343  sub IN_COLUMN_GROUP_IM () { 0b10 } Line 948  sub IN_COLUMN_GROUP_IM () { 0b10 }
948  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
949    my $self = shift;    my $self = shift;
950    $self->{state} = DATA_STATE; # MUST    $self->{state} = DATA_STATE; # MUST
951      #$self->{s_kwd}; # state keyword - initialized when used
952      #$self->{entity__value}; # initialized when used
953      #$self->{entity__match}; # initialized when used
954    $self->{content_model} = PCDATA_CONTENT_MODEL; # be    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
955    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    undef $self->{ct}; # current token
956    undef $self->{current_attribute};    undef $self->{ca}; # current attribute
957    undef $self->{last_emitted_start_tag_name};    undef $self->{last_stag_name}; # last emitted start tag name
958    undef $self->{last_attribute_value_state};    #$self->{prev_state}; # initialized when used
959    $self->{char} = [];    delete $self->{self_closing};
960    # $self->{next_char}    $self->{char_buffer} = '';
961      $self->{char_buffer_pos} = 0;
962      $self->{nc} = -1; # next input character
963      #$self->{next_nc}
964    !!!next-input-character;    !!!next-input-character;
965    $self->{token} = [];    $self->{token} = [];
966    # $self->{escape}    # $self->{escape}
# Line 360  sub _initialize_tokenizer ($) { Line 971  sub _initialize_tokenizer ($) {
971  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
972  ##   ->{name} (DOCTYPE_TOKEN)  ##   ->{name} (DOCTYPE_TOKEN)
973  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
974  ##   ->{public_identifier} (DOCTYPE_TOKEN)  ##   ->{pubid} (DOCTYPE_TOKEN)
975  ##   ->{system_identifier} (DOCTYPE_TOKEN)  ##   ->{sysid} (DOCTYPE_TOKEN)
976  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
977  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
978  ##        ->{name}  ##        ->{name}
979  ##        ->{value}  ##        ->{value}
980  ##        ->{has_reference} == 1 or 0  ##        ->{has_reference} == 1 or 0
981  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
982    ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
983    ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
984    ##     while the token is pushed back to the stack.
985    
986  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
987    
# Line 377  sub _initialize_tokenizer ($) { Line 991  sub _initialize_tokenizer ($) {
991  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
992  ## and removed from the list.  ## and removed from the list.
993    
994  ## NOTE: HTML5 "Writing HTML documents" section, applied to  ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
995  ## documents and not to user agents and conformance checkers,  ## (This requirement was dropped from HTML5 spec, unfortunately.)
996  ## contains some requirements that are not detected by the  
997  ## parsing algorithm:  my $is_space = {
998  ## - Some requirements on character encoding declarations. ## TODO    0x0009 => 1, # CHARACTER TABULATION (HT)
999  ## - "Elements MUST NOT contain content that their content model disallows."    0x000A => 1, # LINE FEED (LF)
1000  ##   ... Some are parse error, some are not (will be reported by c.c.).    #0x000B => 0, # LINE TABULATION (VT)
1001  ## - Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) ## TODO    0x000C => 1, # FORM FEED (FF)
1002  ## - Text (in elements, attributes, and comments) SHOULD NOT contain    #0x000D => 1, # CARRIAGE RETURN (CR)
1003  ##   control characters other than space characters. ## TODO: (what is control character? C0, C1 and DEL?  Unicode control character?)    0x0020 => 1, # SPACE (SP)
1004    };
 ## TODO: HTML5 poses authors two SHOULD-level requirements that cannot  
 ## be detected by the HTML5 parsing algorithm:  
 ## - Text,  
1005    
1006  sub _get_next_token ($) {  sub _get_next_token ($) {
1007    my $self = shift;    my $self = shift;
1008    
1009      if ($self->{self_closing}) {
1010        !!!parse-error (type => 'nestc', token => $self->{ct});
1011        ## NOTE: The |self_closing| flag is only set by start tag token.
1012        ## In addition, when a start tag token is emitted, it is always set to
1013        ## |ct|.
1014        delete $self->{self_closing};
1015      }
1016    
1017    if (@{$self->{token}}) {    if (@{$self->{token}}) {
1018        $self->{self_closing} = $self->{token}->[0]->{self_closing};
1019      return shift @{$self->{token}};      return shift @{$self->{token}};
1020    }    }
1021    
1022    A: {    A: {
1023      if ($self->{state} == DATA_STATE) {      if ($self->{state} == PCDATA_STATE) {
1024        if ($self->{next_char} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1025    
1026          if ($self->{nc} == 0x0026) { # &
1027            !!!cp (0.1);
1028            ## NOTE: In the spec, the tokenizer is switched to the
1029            ## "entity data state".  In this implementation, the tokenizer
1030            ## is switched to the |ENTITY_STATE|, which is an implementation
1031            ## of the "consume a character reference" algorithm.
1032            $self->{entity_add} = -1;
1033            $self->{prev_state} = DATA_STATE;
1034            $self->{state} = ENTITY_STATE;
1035            !!!next-input-character;
1036            redo A;
1037          } elsif ($self->{nc} == 0x003C) { # <
1038            !!!cp (0.2);
1039            $self->{state} = TAG_OPEN_STATE;
1040            !!!next-input-character;
1041            redo A;
1042          } elsif ($self->{nc} == -1) {
1043            !!!cp (0.3);
1044            !!!emit ({type => END_OF_FILE_TOKEN,
1045                      line => $self->{line}, column => $self->{column}});
1046            last A; ## TODO: ok?
1047          } else {
1048            !!!cp (0.4);
1049            #
1050          }
1051    
1052          # Anything else
1053          my $token = {type => CHARACTER_TOKEN,
1054                       data => chr $self->{nc},
1055                       line => $self->{line}, column => $self->{column},
1056                      };
1057          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1058    
1059          ## Stay in the state.
1060          !!!next-input-character;
1061          !!!emit ($token);
1062          redo A;
1063        } elsif ($self->{state} == DATA_STATE) {
1064          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1065          if ($self->{nc} == 0x0026) { # &
1066            $self->{s_kwd} = '';
1067          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1068              not $self->{escape}) {              not $self->{escape}) {
1069            !!!cp (1);            !!!cp (1);
1070            $self->{state} = ENTITY_DATA_STATE;            ## NOTE: In the spec, the tokenizer is switched to the
1071              ## "entity data state".  In this implementation, the tokenizer
1072              ## is switched to the |ENTITY_STATE|, which is an implementation
1073              ## of the "consume a character reference" algorithm.
1074              $self->{entity_add} = -1;
1075              $self->{prev_state} = DATA_STATE;
1076              $self->{state} = ENTITY_STATE;
1077            !!!next-input-character;            !!!next-input-character;
1078            redo A;            redo A;
1079          } else {          } else {
1080            !!!cp (2);            !!!cp (2);
1081            #            #
1082          }          }
1083        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
1084          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1085            unless ($self->{escape}) {            $self->{s_kwd} .= '-';
1086              if ($self->{prev_char}->[0] == 0x002D and # -            
1087                  $self->{prev_char}->[1] == 0x0021 and # !            if ($self->{s_kwd} eq '<!--') {
1088                  $self->{prev_char}->[2] == 0x003C) { # <              !!!cp (3);
1089                !!!cp (3);              $self->{escape} = 1; # unless $self->{escape};
1090                $self->{escape} = 1;              $self->{s_kwd} = '--';
1091              } else {              #
1092                !!!cp (4);            } elsif ($self->{s_kwd} eq '---') {
1093              }              !!!cp (4);
1094                $self->{s_kwd} = '--';
1095                #
1096            } else {            } else {
1097              !!!cp (5);              !!!cp (5);
1098                #
1099            }            }
1100          }          }
1101                    
1102          #          #
1103        } elsif ($self->{next_char} == 0x003C) { # <        } elsif ($self->{nc} == 0x0021) { # !
1104            if (length $self->{s_kwd}) {
1105              !!!cp (5.1);
1106              $self->{s_kwd} .= '!';
1107              #
1108            } else {
1109              !!!cp (5.2);
1110              #$self->{s_kwd} = '';
1111              #
1112            }
1113            #
1114          } elsif ($self->{nc} == 0x003C) { # <
1115          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1116              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1117               not $self->{escape})) {               not $self->{escape})) {
# Line 438  sub _get_next_token ($) { Line 1121  sub _get_next_token ($) {
1121            redo A;            redo A;
1122          } else {          } else {
1123            !!!cp (7);            !!!cp (7);
1124              $self->{s_kwd} = '';
1125            #            #
1126          }          }
1127        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1128          if ($self->{escape} and          if ($self->{escape} and
1129              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1130            if ($self->{prev_char}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '--') {
               $self->{prev_char}->[1] == 0x002D) { # -  
1131              !!!cp (8);              !!!cp (8);
1132              delete $self->{escape};              delete $self->{escape};
1133            } else {            } else {
# Line 454  sub _get_next_token ($) { Line 1137  sub _get_next_token ($) {
1137            !!!cp (10);            !!!cp (10);
1138          }          }
1139                    
1140            $self->{s_kwd} = '';
1141          #          #
1142        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1143          !!!cp (11);          !!!cp (11);
1144            $self->{s_kwd} = '';
1145          !!!emit ({type => END_OF_FILE_TOKEN,          !!!emit ({type => END_OF_FILE_TOKEN,
1146                    line => $self->{line}, column => $self->{column}});                    line => $self->{line}, column => $self->{column}});
1147          last A; ## TODO: ok?          last A; ## TODO: ok?
1148        } else {        } else {
1149          !!!cp (12);          !!!cp (12);
1150            $self->{s_kwd} = '';
1151            #
1152        }        }
1153    
1154        # Anything else        # Anything else
1155        my $token = {type => CHARACTER_TOKEN,        my $token = {type => CHARACTER_TOKEN,
1156                     data => chr $self->{next_char},                     data => chr $self->{nc},
1157                     line => $self->{line}, column => $self->{column}};                     line => $self->{line}, column => $self->{column},
1158        ## Stay in the data state                    };
1159        !!!next-input-character;        if ($self->{read_until}->($token->{data}, q[-!<>&],
1160                                    length $token->{data})) {
1161        !!!emit ($token);          $self->{s_kwd} = '';
1162          }
       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  
1163    
1164        unless (defined $token) {        ## Stay in the data state.
1165          if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1166          !!!cp (13);          !!!cp (13);
1167          !!!emit ({type => CHARACTER_TOKEN, data => '&',          $self->{state} = PCDATA_STATE;
                   line => $l, column => $c});  
1168        } else {        } else {
1169          !!!cp (14);          !!!cp (14);
1170          !!!emit ($token);          ## Stay in the state.
1171        }        }
1172          !!!next-input-character;
1173          !!!emit ($token);
1174        redo A;        redo A;
1175      } elsif ($self->{state} == TAG_OPEN_STATE) {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1176        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1177          if ($self->{next_char} == 0x002F) { # /          if ($self->{nc} == 0x002F) { # /
1178            !!!cp (15);            !!!cp (15);
1179            !!!next-input-character;            !!!next-input-character;
1180            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1181            redo A;            redo A;
1182            } elsif ($self->{nc} == 0x0021) { # !
1183              !!!cp (15.1);
1184              $self->{s_kwd} = '<' unless $self->{escape};
1185              #
1186          } else {          } else {
1187            !!!cp (16);            !!!cp (16);
1188            ## reconsume            #
           $self->{state} = DATA_STATE;  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev}});  
   
           redo A;  
1189          }          }
1190    
1191            ## reconsume
1192            $self->{state} = DATA_STATE;
1193            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1194                      line => $self->{line_prev},
1195                      column => $self->{column_prev},
1196                     });
1197            redo A;
1198        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1199          if ($self->{next_char} == 0x0021) { # !          if ($self->{nc} == 0x0021) { # !
1200            !!!cp (17);            !!!cp (17);
1201            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1202            !!!next-input-character;            !!!next-input-character;
1203            redo A;            redo A;
1204          } elsif ($self->{next_char} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1205            !!!cp (18);            !!!cp (18);
1206            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1207            !!!next-input-character;            !!!next-input-character;
1208            redo A;            redo A;
1209          } elsif (0x0041 <= $self->{next_char} and          } elsif (0x0041 <= $self->{nc} and
1210                   $self->{next_char} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1211            !!!cp (19);            !!!cp (19);
1212            $self->{current_token}            $self->{ct}
1213              = {type => START_TAG_TOKEN,              = {type => START_TAG_TOKEN,
1214                 tag_name => chr ($self->{next_char} + 0x0020),                 tag_name => chr ($self->{nc} + 0x0020),
1215                 line => $self->{line_prev},                 line => $self->{line_prev},
1216                 column => $self->{column_prev}};                 column => $self->{column_prev}};
1217            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1218            !!!next-input-character;            !!!next-input-character;
1219            redo A;            redo A;
1220          } elsif (0x0061 <= $self->{next_char} and          } elsif (0x0061 <= $self->{nc} and
1221                   $self->{next_char} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1222            !!!cp (20);            !!!cp (20);
1223            $self->{current_token} = {type => START_TAG_TOKEN,            $self->{ct} = {type => START_TAG_TOKEN,
1224                                      tag_name => chr ($self->{next_char}),                                      tag_name => chr ($self->{nc}),
1225                                      line => $self->{line_prev},                                      line => $self->{line_prev},
1226                                      column => $self->{column_prev}};                                      column => $self->{column_prev}};
1227            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1228            !!!next-input-character;            !!!next-input-character;
1229            redo A;            redo A;
1230          } elsif ($self->{next_char} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1231            !!!cp (21);            !!!cp (21);
1232            !!!parse-error (type => 'empty start tag',            !!!parse-error (type => 'empty start tag',
1233                            line => $self->{line_prev},                            line => $self->{line_prev},
# Line 553  sub _get_next_token ($) { Line 1237  sub _get_next_token ($) {
1237    
1238            !!!emit ({type => CHARACTER_TOKEN, data => '<>',            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
1239                      line => $self->{line_prev},                      line => $self->{line_prev},
1240                      column => $self->{column_prev}});                      column => $self->{column_prev},
1241                       });
1242    
1243            redo A;            redo A;
1244          } elsif ($self->{next_char} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1245            !!!cp (22);            !!!cp (22);
1246            !!!parse-error (type => 'pio',            !!!parse-error (type => 'pio',
1247                            line => $self->{line_prev},                            line => $self->{line_prev},
1248                            column => $self->{column_prev});                            column => $self->{column_prev});
1249            $self->{state} = BOGUS_COMMENT_STATE;            $self->{state} = BOGUS_COMMENT_STATE;
1250            $self->{current_token} = {type => COMMENT_TOKEN, data => '',            $self->{ct} = {type => COMMENT_TOKEN, data => '',
1251                                      line => $self->{line_prev},                                      line => $self->{line_prev},
1252                                      column => $self->{column_prev}};                                      column => $self->{column_prev},
1253            ## $self->{next_char} is intentionally left as is                                     };
1254              ## $self->{nc} is intentionally left as is
1255            redo A;            redo A;
1256          } else {          } else {
1257            !!!cp (23);            !!!cp (23);
1258            !!!parse-error (type => 'bare stago');            !!!parse-error (type => 'bare stago',
1259                              line => $self->{line_prev},
1260                              column => $self->{column_prev});
1261            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1262            ## reconsume            ## reconsume
1263    
1264            !!!emit ({type => CHARACTER_TOKEN, data => '<',            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1265                      line => $self->{line_prev},                      line => $self->{line_prev},
1266                      column => $self->{column_prev}});                      column => $self->{column_prev},
1267                       });
1268    
1269            redo A;            redo A;
1270          }          }
# Line 583  sub _get_next_token ($) { Line 1272  sub _get_next_token ($) {
1272          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
1273        }        }
1274      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1275          ## NOTE: The "close tag open state" in the spec is implemented as
1276          ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
1277    
1278        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1279        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1280          if (defined $self->{last_emitted_start_tag_name}) {          if (defined $self->{last_stag_name}) {
1281              $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1282            ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>            $self->{s_kwd} = '';
1283            my @next_char;            ## Reconsume.
1284            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...  
           }  
1285          } else {          } else {
1286            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1287              ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1288            !!!cp (28);            !!!cp (28);
           # next-input-character is already done  
1289            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1290              ## Reconsume.
1291            !!!emit ({type => CHARACTER_TOKEN, data => '</',            !!!emit ({type => CHARACTER_TOKEN, data => '</',
1292                      line => $l, column => $c});                      line => $l, column => $c,
1293                       });
1294            redo A;            redo A;
1295          }          }
1296        }        }
1297          
1298        if (0x0041 <= $self->{next_char} and        if (0x0041 <= $self->{nc} and
1299            $self->{next_char} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1300          !!!cp (29);          !!!cp (29);
1301          $self->{current_token}          $self->{ct}
1302              = {type => END_TAG_TOKEN,              = {type => END_TAG_TOKEN,
1303                 tag_name => chr ($self->{next_char} + 0x0020),                 tag_name => chr ($self->{nc} + 0x0020),
1304                 line => $l, column => $c};                 line => $l, column => $c};
1305          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1306          !!!next-input-character;          !!!next-input-character;
1307          redo A;          redo A;
1308        } elsif (0x0061 <= $self->{next_char} and        } elsif (0x0061 <= $self->{nc} and
1309                 $self->{next_char} <= 0x007A) { # a..z                 $self->{nc} <= 0x007A) { # a..z
1310          !!!cp (30);          !!!cp (30);
1311          $self->{current_token} = {type => END_TAG_TOKEN,          $self->{ct} = {type => END_TAG_TOKEN,
1312                                    tag_name => chr ($self->{next_char}),                                    tag_name => chr ($self->{nc}),
1313                                    line => $l, column => $c};                                    line => $l, column => $c};
1314          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1315          !!!next-input-character;          !!!next-input-character;
1316          redo A;          redo A;
1317        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1318          !!!cp (31);          !!!cp (31);
1319          !!!parse-error (type => 'empty end tag',          !!!parse-error (type => 'empty end tag',
1320                          line => $self->{line_prev}, ## "<" in "</>"                          line => $self->{line_prev}, ## "<" in "</>"
# Line 670  sub _get_next_token ($) { Line 1322  sub _get_next_token ($) {
1322          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1323          !!!next-input-character;          !!!next-input-character;
1324          redo A;          redo A;
1325        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1326          !!!cp (32);          !!!cp (32);
1327          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1328          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1329          # reconsume          # reconsume
1330    
1331          !!!emit ({type => CHARACTER_TOKEN, data => '</',          !!!emit ({type => CHARACTER_TOKEN, data => '</',
1332                    line => $l, column => $c});                    line => $l, column => $c,
1333                     });
1334    
1335          redo A;          redo A;
1336        } else {        } else {
1337          !!!cp (33);          !!!cp (33);
1338          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1339          $self->{state} = BOGUS_COMMENT_STATE;          $self->{state} = BOGUS_COMMENT_STATE;
1340          $self->{current_token} = {type => COMMENT_TOKEN, data => '',          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1341                                    line => $self->{line_prev}, # "<" of "</"                                    line => $self->{line_prev}, # "<" of "</"
1342                                    column => $self->{column_prev} - 1};                                    column => $self->{column_prev} - 1,
1343          ## $self->{next_char} is intentionally left as is                                   };
1344          redo A;          ## NOTE: $self->{nc} is intentionally left as is.
1345            ## Although the "anything else" case of the spec not explicitly
1346            ## states that the next input character is to be reconsumed,
1347            ## it will be included to the |data| of the comment token
1348            ## generated from the bogus end tag, as defined in the
1349            ## "bogus comment state" entry.
1350            redo A;
1351          }
1352        } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1353          my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1354          if (length $ch) {
1355            my $CH = $ch;
1356            $ch =~ tr/a-z/A-Z/;
1357            my $nch = chr $self->{nc};
1358            if ($nch eq $ch or $nch eq $CH) {
1359              !!!cp (24);
1360              ## Stay in the state.
1361              $self->{s_kwd} .= $nch;
1362              !!!next-input-character;
1363              redo A;
1364            } else {
1365              !!!cp (25);
1366              $self->{state} = DATA_STATE;
1367              ## Reconsume.
1368              !!!emit ({type => CHARACTER_TOKEN,
1369                        data => '</' . $self->{s_kwd},
1370                        line => $self->{line_prev},
1371                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1372                       });
1373              redo A;
1374            }
1375          } else { # after "<{tag-name}"
1376            unless ($is_space->{$self->{nc}} or
1377                    {
1378                     0x003E => 1, # >
1379                     0x002F => 1, # /
1380                     -1 => 1, # EOF
1381                    }->{$self->{nc}}) {
1382              !!!cp (26);
1383              ## Reconsume.
1384              $self->{state} = DATA_STATE;
1385              !!!emit ({type => CHARACTER_TOKEN,
1386                        data => '</' . $self->{s_kwd},
1387                        line => $self->{line_prev},
1388                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1389                       });
1390              redo A;
1391            } else {
1392              !!!cp (27);
1393              $self->{ct}
1394                  = {type => END_TAG_TOKEN,
1395                     tag_name => $self->{last_stag_name},
1396                     line => $self->{line_prev},
1397                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1398              $self->{state} = TAG_NAME_STATE;
1399              ## Reconsume.
1400              redo A;
1401            }
1402        }        }
1403      } elsif ($self->{state} == TAG_NAME_STATE) {      } elsif ($self->{state} == TAG_NAME_STATE) {
1404        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  
1405          !!!cp (34);          !!!cp (34);
1406          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1407          !!!next-input-character;          !!!next-input-character;
1408          redo A;          redo A;
1409        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1410          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1411            !!!cp (35);            !!!cp (35);
1412            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1413                = 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) {  
1414            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1415            #if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1416            #  ## NOTE: This should never be reached.            #  ## NOTE: This should never be reached.
1417            #  !!! cp (36);            #  !!! cp (36);
1418            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 716  sub _get_next_token ($) { Line 1420  sub _get_next_token ($) {
1420              !!!cp (37);              !!!cp (37);
1421            #}            #}
1422          } else {          } else {
1423            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1424          }          }
1425          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1426          !!!next-input-character;          !!!next-input-character;
1427    
1428          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1429    
1430          redo A;          redo A;
1431        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1432                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1433          !!!cp (38);          !!!cp (38);
1434          $self->{current_token}->{tag_name} .= chr ($self->{next_char} + 0x0020);          $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1435            # start tag or end tag            # start tag or end tag
1436          ## Stay in this state          ## Stay in this state
1437          !!!next-input-character;          !!!next-input-character;
1438          redo A;          redo A;
1439        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1440          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1441          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1442            !!!cp (39);            !!!cp (39);
1443            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1444                = 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) {  
1445            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1446            #if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1447            #  ## NOTE: This state should never be reached.            #  ## NOTE: This state should never be reached.
1448            #  !!! cp (40);            #  !!! cp (40);
1449            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 749  sub _get_next_token ($) { Line 1451  sub _get_next_token ($) {
1451              !!!cp (41);              !!!cp (41);
1452            #}            #}
1453          } else {          } else {
1454            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1455          }          }
1456          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1457          # reconsume          # reconsume
1458    
1459          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1460    
1461          redo A;          redo A;
1462        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1463            !!!cp (42);
1464            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1465          !!!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  
1466          redo A;          redo A;
1467        } else {        } else {
1468          !!!cp (44);          !!!cp (44);
1469          $self->{current_token}->{tag_name} .= chr $self->{next_char};          $self->{ct}->{tag_name} .= chr $self->{nc};
1470            # start tag or end tag            # start tag or end tag
1471          ## Stay in the state          ## Stay in the state
1472          !!!next-input-character;          !!!next-input-character;
1473          redo A;          redo A;
1474        }        }
1475      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1476        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  
1477          !!!cp (45);          !!!cp (45);
1478          ## Stay in the state          ## Stay in the state
1479          !!!next-input-character;          !!!next-input-character;
1480          redo A;          redo A;
1481        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1482          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1483            !!!cp (46);            !!!cp (46);
1484            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1485                = 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) {  
1486            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1487            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1488              !!!cp (47);              !!!cp (47);
1489              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1490            } else {            } else {
1491              !!!cp (48);              !!!cp (48);
1492            }            }
1493          } else {          } else {
1494            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1495          }          }
1496          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1497          !!!next-input-character;          !!!next-input-character;
1498    
1499          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1500    
1501          redo A;          redo A;
1502        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1503                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1504          !!!cp (49);          !!!cp (49);
1505          $self->{current_attribute} = {name => chr ($self->{next_char} + 0x0020),          $self->{ca}
1506                                value => ''};              = {name => chr ($self->{nc} + 0x0020),
1507                   value => '',
1508                   line => $self->{line}, column => $self->{column}};
1509          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1510          !!!next-input-character;          !!!next-input-character;
1511          redo A;          redo A;
1512        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1513            !!!cp (50);
1514            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1515          !!!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  
1516          redo A;          redo A;
1517        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1518          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1519          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1520            !!!cp (52);            !!!cp (52);
1521            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1522                = 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) {  
1523            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1524            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1525              !!!cp (53);              !!!cp (53);
1526              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1527            } else {            } else {
1528              !!!cp (54);              !!!cp (54);
1529            }            }
1530          } else {          } else {
1531            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1532          }          }
1533          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1534          # reconsume          # reconsume
1535    
1536          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1537    
1538          redo A;          redo A;
1539        } else {        } else {
# Line 865  sub _get_next_token ($) { Line 1541  sub _get_next_token ($) {
1541               0x0022 => 1, # "               0x0022 => 1, # "
1542               0x0027 => 1, # '               0x0027 => 1, # '
1543               0x003D => 1, # =               0x003D => 1, # =
1544              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1545            !!!cp (55);            !!!cp (55);
1546            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1547          } else {          } else {
1548            !!!cp (56);            !!!cp (56);
1549          }          }
1550          $self->{current_attribute} = {name => chr ($self->{next_char}),          $self->{ca}
1551                                value => ''};              = {name => chr ($self->{nc}),
1552                   value => '',
1553                   line => $self->{line}, column => $self->{column}};
1554          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1555          !!!next-input-character;          !!!next-input-character;
1556          redo A;          redo A;
1557        }        }
1558      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1559        my $before_leave = sub {        my $before_leave = sub {
1560          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1561              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1562            !!!cp (57);            !!!cp (57);
1563            !!!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});
1564            ## Discard $self->{current_attribute} # MUST            ## Discard $self->{ca} # MUST
1565          } else {          } else {
1566            !!!cp (58);            !!!cp (58);
1567            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            $self->{ct}->{attributes}->{$self->{ca}->{name}}
1568              = $self->{current_attribute};              = $self->{ca};
1569          }          }
1570        }; # $before_leave        }; # $before_leave
1571    
1572        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  
1573          !!!cp (59);          !!!cp (59);
1574          $before_leave->();          $before_leave->();
1575          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1576          !!!next-input-character;          !!!next-input-character;
1577          redo A;          redo A;
1578        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1579          !!!cp (60);          !!!cp (60);
1580          $before_leave->();          $before_leave->();
1581          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1582          !!!next-input-character;          !!!next-input-character;
1583          redo A;          redo A;
1584        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1585          $before_leave->();          $before_leave->();
1586          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1587            !!!cp (61);            !!!cp (61);
1588            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1589                = 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) {  
1590            !!!cp (62);            !!!cp (62);
1591            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1592            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1593              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1594            }            }
1595          } else {          } else {
1596            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1597          }          }
1598          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1599          !!!next-input-character;          !!!next-input-character;
1600    
1601          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1602    
1603          redo A;          redo A;
1604        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1605                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1606          !!!cp (63);          !!!cp (63);
1607          $self->{current_attribute}->{name} .= chr ($self->{next_char} + 0x0020);          $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1608          ## Stay in the state          ## Stay in the state
1609          !!!next-input-character;          !!!next-input-character;
1610          redo A;          redo A;
1611        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1612            !!!cp (64);
1613          $before_leave->();          $before_leave->();
1614            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1615          !!!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  
1616          redo A;          redo A;
1617        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1618          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1619          $before_leave->();          $before_leave->();
1620          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1621            !!!cp (66);            !!!cp (66);
1622            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1623                = 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) {  
1624            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1625            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1626              !!!cp (67);              !!!cp (67);
1627              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1628            } else {            } else {
# Line 970  sub _get_next_token ($) { Line 1630  sub _get_next_token ($) {
1630              !!!cp (68);              !!!cp (68);
1631            }            }
1632          } else {          } else {
1633            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1634          }          }
1635          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1636          # reconsume          # reconsume
1637    
1638          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1639    
1640          redo A;          redo A;
1641        } else {        } else {
1642          if ($self->{next_char} == 0x0022 or # "          if ($self->{nc} == 0x0022 or # "
1643              $self->{next_char} == 0x0027) { # '              $self->{nc} == 0x0027) { # '
1644            !!!cp (69);            !!!cp (69);
1645            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1646          } else {          } else {
1647            !!!cp (70);            !!!cp (70);
1648          }          }
1649          $self->{current_attribute}->{name} .= chr ($self->{next_char});          $self->{ca}->{name} .= chr ($self->{nc});
1650          ## Stay in the state          ## Stay in the state
1651          !!!next-input-character;          !!!next-input-character;
1652          redo A;          redo A;
1653        }        }
1654      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1655        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  
1656          !!!cp (71);          !!!cp (71);
1657          ## Stay in the state          ## Stay in the state
1658          !!!next-input-character;          !!!next-input-character;
1659          redo A;          redo A;
1660        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1661          !!!cp (72);          !!!cp (72);
1662          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1663          !!!next-input-character;          !!!next-input-character;
1664          redo A;          redo A;
1665        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1666          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1667            !!!cp (73);            !!!cp (73);
1668            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1669                = 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) {  
1670            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1671            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1672              !!!cp (74);              !!!cp (74);
1673              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1674            } else {            } else {
# Line 1022  sub _get_next_token ($) { Line 1676  sub _get_next_token ($) {
1676              !!!cp (75);              !!!cp (75);
1677            }            }
1678          } else {          } else {
1679            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1680          }          }
1681          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1682          !!!next-input-character;          !!!next-input-character;
1683    
1684          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1685    
1686          redo A;          redo A;
1687        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1688                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1689          !!!cp (76);          !!!cp (76);
1690          $self->{current_attribute} = {name => chr ($self->{next_char} + 0x0020),          $self->{ca}
1691                                value => ''};              = {name => chr ($self->{nc} + 0x0020),
1692                   value => '',
1693                   line => $self->{line}, column => $self->{column}};
1694          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1695          !!!next-input-character;          !!!next-input-character;
1696          redo A;          redo A;
1697        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1698            !!!cp (77);
1699            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1700          !!!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  
1701          redo A;          redo A;
1702        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1703          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1704          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1705            !!!cp (79);            !!!cp (79);
1706            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1707                = 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) {  
1708            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1709            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1710              !!!cp (80);              !!!cp (80);
1711              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1712            } else {            } else {
# Line 1071  sub _get_next_token ($) { Line 1714  sub _get_next_token ($) {
1714              !!!cp (81);              !!!cp (81);
1715            }            }
1716          } else {          } else {
1717            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1718          }          }
1719          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1720          # reconsume          # reconsume
1721    
1722          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1723    
1724          redo A;          redo A;
1725        } else {        } else {
1726          !!!cp (82);          if ($self->{nc} == 0x0022 or # "
1727          $self->{current_attribute} = {name => chr ($self->{next_char}),              $self->{nc} == 0x0027) { # '
1728                                value => ''};            !!!cp (78);
1729              !!!parse-error (type => 'bad attribute name');
1730            } else {
1731              !!!cp (82);
1732            }
1733            $self->{ca}
1734                = {name => chr ($self->{nc}),
1735                   value => '',
1736                   line => $self->{line}, column => $self->{column}};
1737          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1738          !!!next-input-character;          !!!next-input-character;
1739          redo A;                  redo A;        
1740        }        }
1741      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1742        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        
1743          !!!cp (83);          !!!cp (83);
1744          ## Stay in the state          ## Stay in the state
1745          !!!next-input-character;          !!!next-input-character;
1746          redo A;          redo A;
1747        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1748          !!!cp (84);          !!!cp (84);
1749          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1750          !!!next-input-character;          !!!next-input-character;
1751          redo A;          redo A;
1752        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1753          !!!cp (85);          !!!cp (85);
1754          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1755          ## reconsume          ## reconsume
1756          redo A;          redo A;
1757        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1758          !!!cp (86);          !!!cp (86);
1759          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1760          !!!next-input-character;          !!!next-input-character;
1761          redo A;          redo A;
1762        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1763          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          !!!parse-error (type => 'empty unquoted attribute value');
1764            if ($self->{ct}->{type} == START_TAG_TOKEN) {
1765            !!!cp (87);            !!!cp (87);
1766            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1767                = 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) {  
1768            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1769            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1770              !!!cp (88);              !!!cp (88);
1771              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1772            } else {            } else {
# Line 1128  sub _get_next_token ($) { Line 1774  sub _get_next_token ($) {
1774              !!!cp (89);              !!!cp (89);
1775            }            }
1776          } else {          } else {
1777            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1778          }          }
1779          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1780          !!!next-input-character;          !!!next-input-character;
1781    
1782          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1783    
1784          redo A;          redo A;
1785        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1786          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1787          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1788            !!!cp (90);            !!!cp (90);
1789            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1790                = 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) {  
1791            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1792            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1793              !!!cp (91);              !!!cp (91);
1794              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1795            } else {            } else {
# Line 1153  sub _get_next_token ($) { Line 1797  sub _get_next_token ($) {
1797              !!!cp (92);              !!!cp (92);
1798            }            }
1799          } else {          } else {
1800            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1801          }          }
1802          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1803          ## reconsume          ## reconsume
1804    
1805          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1806    
1807          redo A;          redo A;
1808        } else {        } else {
1809          if ($self->{next_char} == 0x003D) { # =          if ($self->{nc} == 0x003D) { # =
1810            !!!cp (93);            !!!cp (93);
1811            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1812          } else {          } else {
1813            !!!cp (94);            !!!cp (94);
1814          }          }
1815          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1816          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1817          !!!next-input-character;          !!!next-input-character;
1818          redo A;          redo A;
1819        }        }
1820      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1821        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1822          !!!cp (95);          !!!cp (95);
1823          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1824          !!!next-input-character;          !!!next-input-character;
1825          redo A;          redo A;
1826        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1827          !!!cp (96);          !!!cp (96);
1828          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1829          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1830            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1831            ## implementation of the "consume a character reference" algorithm.
1832            $self->{prev_state} = $self->{state};
1833            $self->{entity_add} = 0x0022; # "
1834            $self->{state} = ENTITY_STATE;
1835          !!!next-input-character;          !!!next-input-character;
1836          redo A;          redo A;
1837        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1838          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1839          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1840            !!!cp (97);            !!!cp (97);
1841            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1842                = 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) {  
1843            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1844            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1845              !!!cp (98);              !!!cp (98);
1846              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1847            } else {            } else {
# Line 1202  sub _get_next_token ($) { Line 1849  sub _get_next_token ($) {
1849              !!!cp (99);              !!!cp (99);
1850            }            }
1851          } else {          } else {
1852            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1853          }          }
1854          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1855          ## reconsume          ## reconsume
1856    
1857          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1858    
1859          redo A;          redo A;
1860        } else {        } else {
1861          !!!cp (100);          !!!cp (100);
1862          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1863            $self->{read_until}->($self->{ca}->{value},
1864                                  q["&],
1865                                  length $self->{ca}->{value});
1866    
1867          ## Stay in the state          ## Stay in the state
1868          !!!next-input-character;          !!!next-input-character;
1869          redo A;          redo A;
1870        }        }
1871      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1872        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1873          !!!cp (101);          !!!cp (101);
1874          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1875          !!!next-input-character;          !!!next-input-character;
1876          redo A;          redo A;
1877        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1878          !!!cp (102);          !!!cp (102);
1879          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1880          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1881            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1882            ## implementation of the "consume a character reference" algorithm.
1883            $self->{entity_add} = 0x0027; # '
1884            $self->{prev_state} = $self->{state};
1885            $self->{state} = ENTITY_STATE;
1886          !!!next-input-character;          !!!next-input-character;
1887          redo A;          redo A;
1888        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1889          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1890          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1891            !!!cp (103);            !!!cp (103);
1892            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1893                = 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) {  
1894            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1895            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1896              !!!cp (104);              !!!cp (104);
1897              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1898            } else {            } else {
# Line 1246  sub _get_next_token ($) { Line 1900  sub _get_next_token ($) {
1900              !!!cp (105);              !!!cp (105);
1901            }            }
1902          } else {          } else {
1903            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1904          }          }
1905          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1906          ## reconsume          ## reconsume
1907    
1908          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1909    
1910          redo A;          redo A;
1911        } else {        } else {
1912          !!!cp (106);          !!!cp (106);
1913          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1914            $self->{read_until}->($self->{ca}->{value},
1915                                  q['&],
1916                                  length $self->{ca}->{value});
1917    
1918          ## Stay in the state          ## Stay in the state
1919          !!!next-input-character;          !!!next-input-character;
1920          redo A;          redo A;
1921        }        }
1922      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1923        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  
1924          !!!cp (107);          !!!cp (107);
1925          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1926          !!!next-input-character;          !!!next-input-character;
1927          redo A;          redo A;
1928        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1929          !!!cp (108);          !!!cp (108);
1930          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1931          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1932            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1933            ## implementation of the "consume a character reference" algorithm.
1934            $self->{entity_add} = -1;
1935            $self->{prev_state} = $self->{state};
1936            $self->{state} = ENTITY_STATE;
1937          !!!next-input-character;          !!!next-input-character;
1938          redo A;          redo A;
1939        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1940          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1941            !!!cp (109);            !!!cp (109);
1942            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1943                = 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) {  
1944            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1945            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1946              !!!cp (110);              !!!cp (110);
1947              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1948            } else {            } else {
# Line 1293  sub _get_next_token ($) { Line 1950  sub _get_next_token ($) {
1950              !!!cp (111);              !!!cp (111);
1951            }            }
1952          } else {          } else {
1953            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1954          }          }
1955          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1956          !!!next-input-character;          !!!next-input-character;
1957    
1958          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1959    
1960          redo A;          redo A;
1961        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1962          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1963          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1964            !!!cp (112);            !!!cp (112);
1965            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1966                = 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) {  
1967            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1968            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1969              !!!cp (113);              !!!cp (113);
1970              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1971            } else {            } else {
# Line 1318  sub _get_next_token ($) { Line 1973  sub _get_next_token ($) {
1973              !!!cp (114);              !!!cp (114);
1974            }            }
1975          } else {          } else {
1976            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1977          }          }
1978          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1979          ## reconsume          ## reconsume
1980    
1981          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1982    
1983          redo A;          redo A;
1984        } else {        } else {
# Line 1331  sub _get_next_token ($) { Line 1986  sub _get_next_token ($) {
1986               0x0022 => 1, # "               0x0022 => 1, # "
1987               0x0027 => 1, # '               0x0027 => 1, # '
1988               0x003D => 1, # =               0x003D => 1, # =
1989              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1990            !!!cp (115);            !!!cp (115);
1991            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1992          } else {          } else {
1993            !!!cp (116);            !!!cp (116);
1994          }          }
1995          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1996            $self->{read_until}->($self->{ca}->{value},
1997                                  q["'=& >],
1998                                  length $self->{ca}->{value});
1999    
2000          ## Stay in the state          ## Stay in the state
2001          !!!next-input-character;          !!!next-input-character;
2002          redo A;          redo A;
2003        }        }
     } 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;  
2004      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2005        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  
2006          !!!cp (118);          !!!cp (118);
2007          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2008          !!!next-input-character;          !!!next-input-character;
2009          redo A;          redo A;
2010        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2011          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2012            !!!cp (119);            !!!cp (119);
2013            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
2014                = 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) {  
2015            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2016            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2017              !!!cp (120);              !!!cp (120);
2018              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2019            } else {            } else {
# Line 1390  sub _get_next_token ($) { Line 2021  sub _get_next_token ($) {
2021              !!!cp (121);              !!!cp (121);
2022            }            }
2023          } else {          } else {
2024            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
2025          }          }
2026          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2027          !!!next-input-character;          !!!next-input-character;
2028    
2029          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2030    
2031          redo A;          redo A;
2032        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
2033            !!!cp (122);
2034            $self->{state} = SELF_CLOSING_START_TAG_STATE;
2035          !!!next-input-character;          !!!next-input-character;
2036          if ($self->{next_char} == 0x003E and # >          redo A;
2037              $self->{current_token}->{type} == START_TAG_TOKEN and        } elsif ($self->{nc} == -1) {
2038              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {          !!!parse-error (type => 'unclosed tag');
2039            # permitted slash          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2040            !!!cp (122);            !!!cp (122.3);
2041            #            $self->{last_stag_name} = $self->{ct}->{tag_name};
2042            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2043              if ($self->{ct}->{attributes}) {
2044                !!!cp (122.1);
2045                !!!parse-error (type => 'end tag attribute');
2046              } else {
2047                ## NOTE: This state should never be reached.
2048                !!!cp (122.2);
2049              }
2050          } else {          } else {
2051            !!!cp (123);            die "$0: $self->{ct}->{type}: Unknown token type";
           !!!parse-error (type => 'nestc');  
2052          }          }
2053          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = DATA_STATE;
2054          # next-input-character is already done          ## Reconsume.
2055            !!!emit ($self->{ct}); # start tag or end tag
2056          redo A;          redo A;
2057        } else {        } else {
2058          !!!cp (124);          !!!cp ('124.1');
2059          !!!parse-error (type => 'no space between attributes');          !!!parse-error (type => 'no space between attributes');
2060          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2061          ## reconsume          ## reconsume
2062          redo A;          redo A;
2063        }        }
2064      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {      } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2065        ## (only happen if PCDATA state)        if ($self->{nc} == 0x003E) { # >
2066                  if ($self->{ct}->{type} == END_TAG_TOKEN) {
2067        ## NOTE: Set by the previous state            !!!cp ('124.2');
2068        #my $token = {type => COMMENT_TOKEN, data => ''};            !!!parse-error (type => 'nestc', token => $self->{ct});
2069              ## TODO: Different type than slash in start tag
2070        BC: {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2071          if ($self->{next_char} == 0x003E) { # >            if ($self->{ct}->{attributes}) {
2072            !!!cp (124);              !!!cp ('124.4');
2073            $self->{state} = DATA_STATE;              !!!parse-error (type => 'end tag attribute');
2074            !!!next-input-character;            } else {
2075                !!!cp ('124.5');
2076            !!!emit ($self->{current_token}); # comment            }
2077              ## TODO: Test |<title></title/>|
2078            } else {
2079              !!!cp ('124.3');
2080              $self->{self_closing} = 1;
2081            }
2082    
2083            redo A;          $self->{state} = DATA_STATE;
2084          } elsif ($self->{next_char} == -1) {          !!!next-input-character;
           !!!cp (125);  
           $self->{state} = DATA_STATE;  
           ## reconsume  
2085    
2086            !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # start tag or end tag
2087    
2088            redo A;          redo A;
2089          } elsif ($self->{nc} == -1) {
2090            !!!parse-error (type => 'unclosed tag');
2091            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2092              !!!cp (124.7);
2093              $self->{last_stag_name} = $self->{ct}->{tag_name};
2094            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2095              if ($self->{ct}->{attributes}) {
2096                !!!cp (124.5);
2097                !!!parse-error (type => 'end tag attribute');
2098              } else {
2099                ## NOTE: This state should never be reached.
2100                !!!cp (124.6);
2101              }
2102          } else {          } else {
2103            !!!cp (126);            die "$0: $self->{ct}->{type}: Unknown token type";
           $self->{current_token}->{data} .= chr ($self->{next_char}); # comment  
           !!!next-input-character;  
           redo BC;  
2104          }          }
2105        } # BC          $self->{state} = DATA_STATE;
2106            ## Reconsume.
2107        die "$0: _get_next_token: unexpected case [BC]";          !!!emit ($self->{ct}); # start tag or end tag
2108      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {          redo A;
2109          } else {
2110            !!!cp ('124.4');
2111            !!!parse-error (type => 'nestc');
2112            ## TODO: This error type is wrong.
2113            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2114            ## Reconsume.
2115            redo A;
2116          }
2117        } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2118        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
2119    
2120        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1);        ## NOTE: Unlike spec's "bogus comment state", this implementation
2121          ## consumes characters one-by-one basis.
       my @next_char;  
       push @next_char, $self->{next_char};  
2122                
2123        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x003E) { # >
2124            !!!cp (124);
2125            $self->{state} = DATA_STATE;
2126          !!!next-input-character;          !!!next-input-character;
2127          push @next_char, $self->{next_char};  
2128          if ($self->{next_char} == 0x002D) { # -          !!!emit ($self->{ct}); # comment
2129            !!!cp (127);          redo A;
2130            $self->{current_token} = {type => COMMENT_TOKEN, data => '',        } elsif ($self->{nc} == -1) {
2131                                      line => $l, column => $c};          !!!cp (125);
2132            $self->{state} = COMMENT_START_STATE;          $self->{state} = DATA_STATE;
2133            !!!next-input-character;          ## reconsume
2134            redo A;  
2135          } else {          !!!emit ($self->{ct}); # comment
2136            !!!cp (128);          redo A;
2137          }        } else {
2138        } elsif ($self->{next_char} == 0x0044 or # D          !!!cp (126);
2139                 $self->{next_char} == 0x0064) { # d          $self->{ct}->{data} .= chr ($self->{nc}); # comment
2140            $self->{read_until}->($self->{ct}->{data},
2141                                  q[>],
2142                                  length $self->{ct}->{data});
2143    
2144            ## Stay in the state.
2145          !!!next-input-character;          !!!next-input-character;
2146          push @next_char, $self->{next_char};          redo A;
2147          if ($self->{next_char} == 0x004F or # O        }
2148              $self->{next_char} == 0x006F) { # o      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2149            !!!next-input-character;        ## (only happen if PCDATA state)
2150            push @next_char, $self->{next_char};        
2151            if ($self->{next_char} == 0x0043 or # C        if ($self->{nc} == 0x002D) { # -
2152                $self->{next_char} == 0x0063) { # c          !!!cp (133);
2153              !!!next-input-character;          $self->{state} = MD_HYPHEN_STATE;
2154              push @next_char, $self->{next_char};          !!!next-input-character;
2155              if ($self->{next_char} == 0x0054 or # T          redo A;
2156                  $self->{next_char} == 0x0074) { # t        } elsif ($self->{nc} == 0x0044 or # D
2157                !!!next-input-character;                 $self->{nc} == 0x0064) { # d
2158                push @next_char, $self->{next_char};          ## ASCII case-insensitive.
2159                if ($self->{next_char} == 0x0059 or # Y          !!!cp (130);
2160                    $self->{next_char} == 0x0079) { # y          $self->{state} = MD_DOCTYPE_STATE;
2161                  !!!next-input-character;          $self->{s_kwd} = chr $self->{nc};
2162                  push @next_char, $self->{next_char};          !!!next-input-character;
2163                  if ($self->{next_char} == 0x0050 or # P          redo A;
2164                      $self->{next_char} == 0x0070) { # p        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2165                    !!!next-input-character;                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2166                    push @next_char, $self->{next_char};                 $self->{nc} == 0x005B) { # [
2167                    if ($self->{next_char} == 0x0045 or # E          !!!cp (135.4);                
2168                        $self->{next_char} == 0x0065) { # e          $self->{state} = MD_CDATA_STATE;
2169                      !!!cp (129);          $self->{s_kwd} = '[';
2170                      ## TODO: What a stupid code this is!          !!!next-input-character;
2171                      $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);  
         }  
2172        } else {        } else {
2173          !!!cp (136);          !!!cp (136);
2174        }        }
2175    
2176        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment',
2177        $self->{next_char} = shift @next_char;                        line => $self->{line_prev},
2178        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2179          ## Reconsume.
2180        $self->{state} = BOGUS_COMMENT_STATE;        $self->{state} = BOGUS_COMMENT_STATE;
2181        $self->{current_token} = {type => COMMENT_TOKEN, data => '',        $self->{ct} = {type => COMMENT_TOKEN, data => '',
2182                                  line => $l, column => $c};                                  line => $self->{line_prev},
2183                                    column => $self->{column_prev} - 1,
2184                                   };
2185        redo A;        redo A;
2186              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2187        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2188        ## 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);
2189            $self->{ct} = {type => COMMENT_TOKEN, data => '',
2190                                      line => $self->{line_prev},
2191                                      column => $self->{column_prev} - 2,
2192                                     };
2193            $self->{state} = COMMENT_START_STATE;
2194            !!!next-input-character;
2195            redo A;
2196          } else {
2197            !!!cp (128);
2198            !!!parse-error (type => 'bogus comment',
2199                            line => $self->{line_prev},
2200                            column => $self->{column_prev} - 2);
2201            $self->{state} = BOGUS_COMMENT_STATE;
2202            ## Reconsume.
2203            $self->{ct} = {type => COMMENT_TOKEN,
2204                                      data => '-',
2205                                      line => $self->{line_prev},
2206                                      column => $self->{column_prev} - 2,
2207                                     };
2208            redo A;
2209          }
2210        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2211          ## ASCII case-insensitive.
2212          if ($self->{nc} == [
2213                undef,
2214                0x004F, # O
2215                0x0043, # C
2216                0x0054, # T
2217                0x0059, # Y
2218                0x0050, # P
2219              ]->[length $self->{s_kwd}] or
2220              $self->{nc} == [
2221                undef,
2222                0x006F, # o
2223                0x0063, # c
2224                0x0074, # t
2225                0x0079, # y
2226                0x0070, # p
2227              ]->[length $self->{s_kwd}]) {
2228            !!!cp (131);
2229            ## Stay in the state.
2230            $self->{s_kwd} .= chr $self->{nc};
2231            !!!next-input-character;
2232            redo A;
2233          } elsif ((length $self->{s_kwd}) == 6 and
2234                   ($self->{nc} == 0x0045 or # E
2235                    $self->{nc} == 0x0065)) { # e
2236            !!!cp (129);
2237            $self->{state} = DOCTYPE_STATE;
2238            $self->{ct} = {type => DOCTYPE_TOKEN,
2239                                      quirks => 1,
2240                                      line => $self->{line_prev},
2241                                      column => $self->{column_prev} - 7,
2242                                     };
2243            !!!next-input-character;
2244            redo A;
2245          } else {
2246            !!!cp (132);        
2247            !!!parse-error (type => 'bogus comment',
2248                            line => $self->{line_prev},
2249                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2250            $self->{state} = BOGUS_COMMENT_STATE;
2251            ## Reconsume.
2252            $self->{ct} = {type => COMMENT_TOKEN,
2253                                      data => $self->{s_kwd},
2254                                      line => $self->{line_prev},
2255                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2256                                     };
2257            redo A;
2258          }
2259        } elsif ($self->{state} == MD_CDATA_STATE) {
2260          if ($self->{nc} == {
2261                '[' => 0x0043, # C
2262                '[C' => 0x0044, # D
2263                '[CD' => 0x0041, # A
2264                '[CDA' => 0x0054, # T
2265                '[CDAT' => 0x0041, # A
2266              }->{$self->{s_kwd}}) {
2267            !!!cp (135.1);
2268            ## Stay in the state.
2269            $self->{s_kwd} .= chr $self->{nc};
2270            !!!next-input-character;
2271            redo A;
2272          } elsif ($self->{s_kwd} eq '[CDATA' and
2273                   $self->{nc} == 0x005B) { # [
2274            !!!cp (135.2);
2275            $self->{ct} = {type => CHARACTER_TOKEN,
2276                                      data => '',
2277                                      line => $self->{line_prev},
2278                                      column => $self->{column_prev} - 7};
2279            $self->{state} = CDATA_SECTION_STATE;
2280            !!!next-input-character;
2281            redo A;
2282          } else {
2283            !!!cp (135.3);
2284            !!!parse-error (type => 'bogus comment',
2285                            line => $self->{line_prev},
2286                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2287            $self->{state} = BOGUS_COMMENT_STATE;
2288            ## Reconsume.
2289            $self->{ct} = {type => COMMENT_TOKEN,
2290                                      data => $self->{s_kwd},
2291                                      line => $self->{line_prev},
2292                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2293                                     };
2294            redo A;
2295          }
2296      } elsif ($self->{state} == COMMENT_START_STATE) {      } elsif ($self->{state} == COMMENT_START_STATE) {
2297        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2298          !!!cp (137);          !!!cp (137);
2299          $self->{state} = COMMENT_START_DASH_STATE;          $self->{state} = COMMENT_START_DASH_STATE;
2300          !!!next-input-character;          !!!next-input-character;
2301          redo A;          redo A;
2302        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2303          !!!cp (138);          !!!cp (138);
2304          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2305          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2306          !!!next-input-character;          !!!next-input-character;
2307    
2308          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2309    
2310          redo A;          redo A;
2311        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2312          !!!cp (139);          !!!cp (139);
2313          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2314          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2315          ## reconsume          ## reconsume
2316    
2317          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2318    
2319          redo A;          redo A;
2320        } else {        } else {
2321          !!!cp (140);          !!!cp (140);
2322          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2323              .= chr ($self->{next_char});              .= chr ($self->{nc});
2324          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2325          !!!next-input-character;          !!!next-input-character;
2326          redo A;          redo A;
2327        }        }
2328      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2329        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2330          !!!cp (141);          !!!cp (141);
2331          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2332          !!!next-input-character;          !!!next-input-character;
2333          redo A;          redo A;
2334        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2335          !!!cp (142);          !!!cp (142);
2336          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2337          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2338          !!!next-input-character;          !!!next-input-character;
2339    
2340          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2341    
2342          redo A;          redo A;
2343        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2344          !!!cp (143);          !!!cp (143);
2345          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2346          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2347          ## reconsume          ## reconsume
2348    
2349          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2350    
2351          redo A;          redo A;
2352        } else {        } else {
2353          !!!cp (144);          !!!cp (144);
2354          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2355              .= '-' . chr ($self->{next_char});              .= '-' . chr ($self->{nc});
2356          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2357          !!!next-input-character;          !!!next-input-character;
2358          redo A;          redo A;
2359        }        }
2360      } elsif ($self->{state} == COMMENT_STATE) {      } elsif ($self->{state} == COMMENT_STATE) {
2361        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2362          !!!cp (145);          !!!cp (145);
2363          $self->{state} = COMMENT_END_DASH_STATE;          $self->{state} = COMMENT_END_DASH_STATE;
2364          !!!next-input-character;          !!!next-input-character;
2365          redo A;          redo A;
2366        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2367          !!!cp (146);          !!!cp (146);
2368          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2369          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2370          ## reconsume          ## reconsume
2371    
2372          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2373    
2374          redo A;          redo A;
2375        } else {        } else {
2376          !!!cp (147);          !!!cp (147);
2377          $self->{current_token}->{data} .= chr ($self->{next_char}); # comment          $self->{ct}->{data} .= chr ($self->{nc}); # comment
2378            $self->{read_until}->($self->{ct}->{data},
2379                                  q[-],
2380                                  length $self->{ct}->{data});
2381    
2382          ## Stay in the state          ## Stay in the state
2383          !!!next-input-character;          !!!next-input-character;
2384          redo A;          redo A;
2385        }        }
2386      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2387        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2388          !!!cp (148);          !!!cp (148);
2389          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2390          !!!next-input-character;          !!!next-input-character;
2391          redo A;          redo A;
2392        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2393          !!!cp (149);          !!!cp (149);
2394          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2395          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2396          ## reconsume          ## reconsume
2397    
2398          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2399    
2400          redo A;          redo A;
2401        } else {        } else {
2402          !!!cp (150);          !!!cp (150);
2403          $self->{current_token}->{data} .= '-' . chr ($self->{next_char}); # comment          $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2404          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2405          !!!next-input-character;          !!!next-input-character;
2406          redo A;          redo A;
2407        }        }
2408      } elsif ($self->{state} == COMMENT_END_STATE) {      } elsif ($self->{state} == COMMENT_END_STATE) {
2409        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2410          !!!cp (151);          !!!cp (151);
2411          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2412          !!!next-input-character;          !!!next-input-character;
2413    
2414          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2415    
2416          redo A;          redo A;
2417        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2418          !!!cp (152);          !!!cp (152);
2419          !!!parse-error (type => 'dash in comment',          !!!parse-error (type => 'dash in comment',
2420                          line => $self->{line_prev},                          line => $self->{line_prev},
2421                          column => $self->{column_prev});                          column => $self->{column_prev});
2422          $self->{current_token}->{data} .= '-'; # comment          $self->{ct}->{data} .= '-'; # comment
2423          ## Stay in the state          ## Stay in the state
2424          !!!next-input-character;          !!!next-input-character;
2425          redo A;          redo A;
2426        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2427          !!!cp (153);          !!!cp (153);
2428          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2429          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2430          ## reconsume          ## reconsume
2431    
2432          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2433    
2434          redo A;          redo A;
2435        } else {        } else {
# Line 1679  sub _get_next_token ($) { Line 2437  sub _get_next_token ($) {
2437          !!!parse-error (type => 'dash in comment',          !!!parse-error (type => 'dash in comment',
2438                          line => $self->{line_prev},                          line => $self->{line_prev},
2439                          column => $self->{column_prev});                          column => $self->{column_prev});
2440          $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment          $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2441          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2442          !!!next-input-character;          !!!next-input-character;
2443          redo A;          redo A;
2444        }        }
2445      } elsif ($self->{state} == DOCTYPE_STATE) {      } elsif ($self->{state} == DOCTYPE_STATE) {
2446        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  
2447          !!!cp (155);          !!!cp (155);
2448          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2449          !!!next-input-character;          !!!next-input-character;
# Line 1702  sub _get_next_token ($) { Line 2456  sub _get_next_token ($) {
2456          redo A;          redo A;
2457        }        }
2458      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2459        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  
2460          !!!cp (157);          !!!cp (157);
2461          ## Stay in the state          ## Stay in the state
2462          !!!next-input-character;          !!!next-input-character;
2463          redo A;          redo A;
2464        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2465          !!!cp (158);          !!!cp (158);
2466          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2467          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2468          !!!next-input-character;          !!!next-input-character;
2469    
2470          !!!emit ($self->{current_token}); # DOCTYPE (quirks)          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2471    
2472          redo A;          redo A;
2473        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2474          !!!cp (159);          !!!cp (159);
2475          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2476          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2477          ## reconsume          ## reconsume
2478    
2479          !!!emit ($self->{current_token}); # DOCTYPE (quirks)          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2480    
2481          redo A;          redo A;
2482        } else {        } else {
2483          !!!cp (160);          !!!cp (160);
2484          $self->{current_token}->{name} = chr $self->{next_char};          $self->{ct}->{name} = chr $self->{nc};
2485          delete $self->{current_token}->{quirks};          delete $self->{ct}->{quirks};
2486  ## ISSUE: "Set the token's name name to the" in the spec  ## ISSUE: "Set the token's name name to the" in the spec
2487          $self->{state} = DOCTYPE_NAME_STATE;          $self->{state} = DOCTYPE_NAME_STATE;
2488          !!!next-input-character;          !!!next-input-character;
# Line 1740  sub _get_next_token ($) { Line 2490  sub _get_next_token ($) {
2490        }        }
2491      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2492  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2493        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  
2494          !!!cp (161);          !!!cp (161);
2495          $self->{state} = AFTER_DOCTYPE_NAME_STATE;          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
2496          !!!next-input-character;          !!!next-input-character;
2497          redo A;          redo A;
2498        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2499          !!!cp (162);          !!!cp (162);
2500          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2501          !!!next-input-character;          !!!next-input-character;
2502    
2503          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2504    
2505          redo A;          redo A;
2506        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2507          !!!cp (163);          !!!cp (163);
2508          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2509          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2510          ## reconsume          ## reconsume
2511    
2512          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2513          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2514    
2515          redo A;          redo A;
2516        } else {        } else {
2517          !!!cp (164);          !!!cp (164);
2518          $self->{current_token}->{name}          $self->{ct}->{name}
2519            .= chr ($self->{next_char}); # DOCTYPE            .= chr ($self->{nc}); # DOCTYPE
2520          ## Stay in the state          ## Stay in the state
2521          !!!next-input-character;          !!!next-input-character;
2522          redo A;          redo A;
2523        }        }
2524      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2525        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  
2526          !!!cp (165);          !!!cp (165);
2527          ## Stay in the state          ## Stay in the state
2528          !!!next-input-character;          !!!next-input-character;
2529          redo A;          redo A;
2530        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2531          !!!cp (166);          !!!cp (166);
2532          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2533          !!!next-input-character;          !!!next-input-character;
2534    
2535          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2536    
2537          redo A;          redo A;
2538        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2539          !!!cp (167);          !!!cp (167);
2540          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2541          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2542          ## reconsume          ## reconsume
2543    
2544          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2545          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2546    
2547          redo A;          redo A;
2548        } elsif ($self->{next_char} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2549                 $self->{next_char} == 0x0070) { # p                 $self->{nc} == 0x0070) { # p
2550            $self->{state} = PUBLIC_STATE;
2551            $self->{s_kwd} = chr $self->{nc};
2552          !!!next-input-character;          !!!next-input-character;
2553          if ($self->{next_char} == 0x0055 or # U          redo A;
2554              $self->{next_char} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2555            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2556            if ($self->{next_char} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2557                $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  
2558          !!!next-input-character;          !!!next-input-character;
2559          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);  
         }  
   
         #  
2560        } else {        } else {
2561          !!!cp (180);          !!!cp (180);
2562            !!!parse-error (type => 'string after DOCTYPE name');
2563            $self->{ct}->{quirks} = 1;
2564    
2565            $self->{state} = BOGUS_DOCTYPE_STATE;
2566          !!!next-input-character;          !!!next-input-character;
2567          #          redo A;
2568        }        }
2569        } elsif ($self->{state} == PUBLIC_STATE) {
2570          ## ASCII case-insensitive
2571          if ($self->{nc} == [
2572                undef,
2573                0x0055, # U
2574                0x0042, # B
2575                0x004C, # L
2576                0x0049, # I
2577              ]->[length $self->{s_kwd}] or
2578              $self->{nc} == [
2579                undef,
2580                0x0075, # u
2581                0x0062, # b
2582                0x006C, # l
2583                0x0069, # i
2584              ]->[length $self->{s_kwd}]) {
2585            !!!cp (175);
2586            ## Stay in the state.
2587            $self->{s_kwd} .= chr $self->{nc};
2588            !!!next-input-character;
2589            redo A;
2590          } elsif ((length $self->{s_kwd}) == 5 and
2591                   ($self->{nc} == 0x0043 or # C
2592                    $self->{nc} == 0x0063)) { # c
2593            !!!cp (168);
2594            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2595            !!!next-input-character;
2596            redo A;
2597          } else {
2598            !!!cp (169);
2599            !!!parse-error (type => 'string after DOCTYPE name',
2600                            line => $self->{line_prev},
2601                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2602            $self->{ct}->{quirks} = 1;
2603    
2604        !!!parse-error (type => 'string after DOCTYPE name');          $self->{state} = BOGUS_DOCTYPE_STATE;
2605        $self->{current_token}->{quirks} = 1;          ## Reconsume.
2606            redo A;
2607          }
2608        } elsif ($self->{state} == SYSTEM_STATE) {
2609          ## ASCII case-insensitive
2610          if ($self->{nc} == [
2611                undef,
2612                0x0059, # Y
2613                0x0053, # S
2614                0x0054, # T
2615                0x0045, # E
2616              ]->[length $self->{s_kwd}] or
2617              $self->{nc} == [
2618                undef,
2619                0x0079, # y
2620                0x0073, # s
2621                0x0074, # t
2622                0x0065, # e
2623              ]->[length $self->{s_kwd}]) {
2624            !!!cp (170);
2625            ## Stay in the state.
2626            $self->{s_kwd} .= chr $self->{nc};
2627            !!!next-input-character;
2628            redo A;
2629          } elsif ((length $self->{s_kwd}) == 5 and
2630                   ($self->{nc} == 0x004D or # M
2631                    $self->{nc} == 0x006D)) { # m
2632            !!!cp (171);
2633            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2634            !!!next-input-character;
2635            redo A;
2636          } else {
2637            !!!cp (172);
2638            !!!parse-error (type => 'string after DOCTYPE name',
2639                            line => $self->{line_prev},
2640                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2641            $self->{ct}->{quirks} = 1;
2642    
2643        $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2644        # next-input-character is already done          ## Reconsume.
2645        redo A;          redo A;
2646          }
2647      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2648        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}}) {  
2649          !!!cp (181);          !!!cp (181);
2650          ## Stay in the state          ## Stay in the state
2651          !!!next-input-character;          !!!next-input-character;
2652          redo A;          redo A;
2653        } elsif ($self->{next_char} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2654          !!!cp (182);          !!!cp (182);
2655          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2656          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2657          !!!next-input-character;          !!!next-input-character;
2658          redo A;          redo A;
2659        } elsif ($self->{next_char} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2660          !!!cp (183);          !!!cp (183);
2661          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2662          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2663          !!!next-input-character;          !!!next-input-character;
2664          redo A;          redo A;
2665        } elsif ($self->{next_char} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2666          !!!cp (184);          !!!cp (184);
2667          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2668    
2669          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2670          !!!next-input-character;          !!!next-input-character;
2671    
2672          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2673          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2674    
2675          redo A;          redo A;
2676        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2677          !!!cp (185);          !!!cp (185);
2678          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2679    
2680          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2681          ## reconsume          ## reconsume
2682    
2683          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2684          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2685    
2686          redo A;          redo A;
2687        } else {        } else {
2688          !!!cp (186);          !!!cp (186);
2689          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2690          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2691    
2692          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2693          !!!next-input-character;          !!!next-input-character;
2694          redo A;          redo A;
2695        }        }
2696      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2697        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2698          !!!cp (187);          !!!cp (187);
2699          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2700          !!!next-input-character;          !!!next-input-character;
2701          redo A;          redo A;
2702        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2703          !!!cp (188);          !!!cp (188);
2704          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2705    
2706          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2707          !!!next-input-character;          !!!next-input-character;
2708    
2709          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2710          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2711    
2712          redo A;          redo A;
2713        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2714          !!!cp (189);          !!!cp (189);
2715          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2716    
2717          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2718          ## reconsume          ## reconsume
2719    
2720          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2721          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2722    
2723          redo A;          redo A;
2724        } else {        } else {
2725          !!!cp (190);          !!!cp (190);
2726          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2727              .= chr $self->{next_char};              .= chr $self->{nc};
2728            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2729                                  length $self->{ct}->{pubid});
2730    
2731          ## Stay in the state          ## Stay in the state
2732          !!!next-input-character;          !!!next-input-character;
2733          redo A;          redo A;
2734        }        }
2735      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2736        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2737          !!!cp (191);          !!!cp (191);
2738          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2739          !!!next-input-character;          !!!next-input-character;
2740          redo A;          redo A;
2741        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2742          !!!cp (192);          !!!cp (192);
2743          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2744    
2745          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2746          !!!next-input-character;          !!!next-input-character;
2747    
2748          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2749          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2750    
2751          redo A;          redo A;
2752        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2753          !!!cp (193);          !!!cp (193);
2754          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2755    
2756          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2757          ## reconsume          ## reconsume
2758    
2759          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2760          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2761    
2762          redo A;          redo A;
2763        } else {        } else {
2764          !!!cp (194);          !!!cp (194);
2765          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2766              .= chr $self->{next_char};              .= chr $self->{nc};
2767            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2768                                  length $self->{ct}->{pubid});
2769    
2770          ## Stay in the state          ## Stay in the state
2771          !!!next-input-character;          !!!next-input-character;
2772          redo A;          redo A;
2773        }        }
2774      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2775        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}}) {  
2776          !!!cp (195);          !!!cp (195);
2777          ## Stay in the state          ## Stay in the state
2778          !!!next-input-character;          !!!next-input-character;
2779          redo A;          redo A;
2780        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2781          !!!cp (196);          !!!cp (196);
2782          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2783          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2784          !!!next-input-character;          !!!next-input-character;
2785          redo A;          redo A;
2786        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2787          !!!cp (197);          !!!cp (197);
2788          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2789          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2790          !!!next-input-character;          !!!next-input-character;
2791          redo A;          redo A;
2792        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2793          !!!cp (198);          !!!cp (198);
2794          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2795          !!!next-input-character;          !!!next-input-character;
2796    
2797          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2798    
2799          redo A;          redo A;
2800        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2801          !!!cp (199);          !!!cp (199);
2802          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2803    
2804          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2805          ## reconsume          ## reconsume
2806    
2807          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2808          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2809    
2810          redo A;          redo A;
2811        } else {        } else {
2812          !!!cp (200);          !!!cp (200);
2813          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2814          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2815    
2816          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2817          !!!next-input-character;          !!!next-input-character;
2818          redo A;          redo A;
2819        }        }
2820      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2821        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}}) {  
2822          !!!cp (201);          !!!cp (201);
2823          ## Stay in the state          ## Stay in the state
2824          !!!next-input-character;          !!!next-input-character;
2825          redo A;          redo A;
2826        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2827          !!!cp (202);          !!!cp (202);
2828          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2829          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2830          !!!next-input-character;          !!!next-input-character;
2831          redo A;          redo A;
2832        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2833          !!!cp (203);          !!!cp (203);
2834          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2835          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2836          !!!next-input-character;          !!!next-input-character;
2837          redo A;          redo A;
2838        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2839          !!!cp (204);          !!!cp (204);
2840          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2841          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2842          !!!next-input-character;          !!!next-input-character;
2843    
2844          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2845          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2846    
2847          redo A;          redo A;
2848        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2849          !!!cp (205);          !!!cp (205);
2850          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2851    
2852          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2853          ## reconsume          ## reconsume
2854    
2855          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2856          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2857    
2858          redo A;          redo A;
2859        } else {        } else {
2860          !!!cp (206);          !!!cp (206);
2861          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2862          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2863    
2864          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2865          !!!next-input-character;          !!!next-input-character;
2866          redo A;          redo A;
2867        }        }
2868      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2869        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2870          !!!cp (207);          !!!cp (207);
2871          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2872          !!!next-input-character;          !!!next-input-character;
2873          redo A;          redo A;
2874        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2875          !!!cp (208);          !!!cp (208);
2876          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2877    
2878          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2879          !!!next-input-character;          !!!next-input-character;
2880    
2881          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2882          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2883    
2884          redo A;          redo A;
2885        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2886          !!!cp (209);          !!!cp (209);
2887          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2888    
2889          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2890          ## reconsume          ## reconsume
2891    
2892          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2893          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2894    
2895          redo A;          redo A;
2896        } else {        } else {
2897          !!!cp (210);          !!!cp (210);
2898          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2899              .= chr $self->{next_char};              .= chr $self->{nc};
2900            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2901                                  length $self->{ct}->{sysid});
2902    
2903          ## Stay in the state          ## Stay in the state
2904          !!!next-input-character;          !!!next-input-character;
2905          redo A;          redo A;
2906        }        }
2907      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2908        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2909          !!!cp (211);          !!!cp (211);
2910          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2911          !!!next-input-character;          !!!next-input-character;
2912          redo A;          redo A;
2913        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2914          !!!cp (212);          !!!cp (212);
2915          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2916    
2917          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2918          !!!next-input-character;          !!!next-input-character;
2919    
2920          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2921          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2922    
2923          redo A;          redo A;
2924        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2925          !!!cp (213);          !!!cp (213);
2926          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2927    
2928          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2929          ## reconsume          ## reconsume
2930    
2931          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2932          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2933    
2934          redo A;          redo A;
2935        } else {        } else {
2936          !!!cp (214);          !!!cp (214);
2937          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2938              .= chr $self->{next_char};              .= chr $self->{nc};
2939            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2940                                  length $self->{ct}->{sysid});
2941    
2942          ## Stay in the state          ## Stay in the state
2943          !!!next-input-character;          !!!next-input-character;
2944          redo A;          redo A;
2945        }        }
2946      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2947        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}}) {  
2948          !!!cp (215);          !!!cp (215);
2949          ## Stay in the state          ## Stay in the state
2950          !!!next-input-character;          !!!next-input-character;
2951          redo A;          redo A;
2952        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2953          !!!cp (216);          !!!cp (216);
2954          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2955          !!!next-input-character;          !!!next-input-character;
2956    
2957          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2958    
2959          redo A;          redo A;
2960        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2961          !!!cp (217);          !!!cp (217);
2962          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
   
2963          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2964          ## reconsume          ## reconsume
2965    
2966          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2967          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2968    
2969          redo A;          redo A;
2970        } else {        } else {
2971          !!!cp (218);          !!!cp (218);
2972          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2973          #$self->{current_token}->{quirks} = 1;          #$self->{ct}->{quirks} = 1;
2974    
2975          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2976          !!!next-input-character;          !!!next-input-character;
2977          redo A;          redo A;
2978        }        }
2979      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2980        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2981          !!!cp (219);          !!!cp (219);
2982          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2983          !!!next-input-character;          !!!next-input-character;
2984    
2985          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2986    
2987          redo A;          redo A;
2988        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2989          !!!cp (220);          !!!cp (220);
2990          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2991          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2992          ## reconsume          ## reconsume
2993    
2994          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2995    
2996          redo A;          redo A;
2997        } else {        } else {
2998          !!!cp (221);          !!!cp (221);
2999            my $s = '';
3000            $self->{read_until}->($s, q[>], 0);
3001    
3002          ## Stay in the state          ## Stay in the state
3003          !!!next-input-character;          !!!next-input-character;
3004          redo A;          redo A;
3005        }        }
3006      } else {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
3007        die "$0: $self->{state}: Unknown state";        ## NOTE: "CDATA section state" in the state is jointly implemented
3008      }        ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3009    } # A          ## and |CDATA_SECTION_MSE2_STATE|.
3010          
3011          if ($self->{nc} == 0x005D) { # ]
3012            !!!cp (221.1);
3013            $self->{state} = CDATA_SECTION_MSE1_STATE;
3014            !!!next-input-character;
3015            redo A;
3016          } elsif ($self->{nc} == -1) {
3017            $self->{state} = DATA_STATE;
3018            !!!next-input-character;
3019            if (length $self->{ct}->{data}) { # character
3020              !!!cp (221.2);
3021              !!!emit ($self->{ct}); # character
3022            } else {
3023              !!!cp (221.3);
3024              ## No token to emit. $self->{ct} is discarded.
3025            }        
3026            redo A;
3027          } else {
3028            !!!cp (221.4);
3029            $self->{ct}->{data} .= chr $self->{nc};
3030            $self->{read_until}->($self->{ct}->{data},
3031                                  q<]>,
3032                                  length $self->{ct}->{data});
3033    
3034    die "$0: _get_next_token: unexpected case";          ## Stay in the state.
3035  } # _get_next_token          !!!next-input-character;
3036            redo A;
3037          }
3038    
3039  sub _tokenize_attempt_to_consume_an_entity ($$$) {        ## ISSUE: "text tokens" in spec.
3040    my ($self, $in_attr, $additional) = @_;      } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3041          if ($self->{nc} == 0x005D) { # ]
3042            !!!cp (221.5);
3043            $self->{state} = CDATA_SECTION_MSE2_STATE;
3044            !!!next-input-character;
3045            redo A;
3046          } else {
3047            !!!cp (221.6);
3048            $self->{ct}->{data} .= ']';
3049            $self->{state} = CDATA_SECTION_STATE;
3050            ## Reconsume.
3051            redo A;
3052          }
3053        } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3054          if ($self->{nc} == 0x003E) { # >
3055            $self->{state} = DATA_STATE;
3056            !!!next-input-character;
3057            if (length $self->{ct}->{data}) { # character
3058              !!!cp (221.7);
3059              !!!emit ($self->{ct}); # character
3060            } else {
3061              !!!cp (221.8);
3062              ## No token to emit. $self->{ct} is discarded.
3063            }
3064            redo A;
3065          } elsif ($self->{nc} == 0x005D) { # ]
3066            !!!cp (221.9); # character
3067            $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3068            ## Stay in the state.
3069            !!!next-input-character;
3070            redo A;
3071          } else {
3072            !!!cp (221.11);
3073            $self->{ct}->{data} .= ']]'; # character
3074            $self->{state} = CDATA_SECTION_STATE;
3075            ## Reconsume.
3076            redo A;
3077          }
3078        } elsif ($self->{state} == ENTITY_STATE) {
3079          if ($is_space->{$self->{nc}} or
3080              {
3081                0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3082                $self->{entity_add} => 1,
3083              }->{$self->{nc}}) {
3084            !!!cp (1001);
3085            ## Don't consume
3086            ## No error
3087            ## Return nothing.
3088            #
3089          } elsif ($self->{nc} == 0x0023) { # #
3090            !!!cp (999);
3091            $self->{state} = ENTITY_HASH_STATE;
3092            $self->{s_kwd} = '#';
3093            !!!next-input-character;
3094            redo A;
3095          } elsif ((0x0041 <= $self->{nc} and
3096                    $self->{nc} <= 0x005A) or # A..Z
3097                   (0x0061 <= $self->{nc} and
3098                    $self->{nc} <= 0x007A)) { # a..z
3099            !!!cp (998);
3100            require Whatpm::_NamedEntityList;
3101            $self->{state} = ENTITY_NAME_STATE;
3102            $self->{s_kwd} = chr $self->{nc};
3103            $self->{entity__value} = $self->{s_kwd};
3104            $self->{entity__match} = 0;
3105            !!!next-input-character;
3106            redo A;
3107          } else {
3108            !!!cp (1027);
3109            !!!parse-error (type => 'bare ero');
3110            ## Return nothing.
3111            #
3112          }
3113    
3114    my ($l, $c) = ($self->{line_prev}, $self->{column_prev});        ## NOTE: No character is consumed by the "consume a character
3115          ## reference" algorithm.  In other word, there is an "&" character
3116          ## that does not introduce a character reference, which would be
3117          ## appended to the parent element or the attribute value in later
3118          ## process of the tokenizer.
3119    
3120          if ($self->{prev_state} == DATA_STATE) {
3121            !!!cp (997);
3122            $self->{state} = $self->{prev_state};
3123            ## Reconsume.
3124            !!!emit ({type => CHARACTER_TOKEN, data => '&',
3125                      line => $self->{line_prev},
3126                      column => $self->{column_prev},
3127                     });
3128            redo A;
3129          } else {
3130            !!!cp (996);
3131            $self->{ca}->{value} .= '&';
3132            $self->{state} = $self->{prev_state};
3133            ## Reconsume.
3134            redo A;
3135          }
3136        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3137          if ($self->{nc} == 0x0078 or # x
3138              $self->{nc} == 0x0058) { # X
3139            !!!cp (995);
3140            $self->{state} = HEXREF_X_STATE;
3141            $self->{s_kwd} .= chr $self->{nc};
3142            !!!next-input-character;
3143            redo A;
3144          } elsif (0x0030 <= $self->{nc} and
3145                   $self->{nc} <= 0x0039) { # 0..9
3146            !!!cp (994);
3147            $self->{state} = NCR_NUM_STATE;
3148            $self->{s_kwd} = $self->{nc} - 0x0030;
3149            !!!next-input-character;
3150            redo A;
3151          } else {
3152            !!!parse-error (type => 'bare nero',
3153                            line => $self->{line_prev},
3154                            column => $self->{column_prev} - 1);
3155    
3156    if ({          ## NOTE: According to the spec algorithm, nothing is returned,
3157         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,          ## and then "&#" is appended to the parent element or the attribute
3158         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR          ## value in the later processing.
3159         $additional => 1,  
3160        }->{$self->{next_char}}) {          if ($self->{prev_state} == DATA_STATE) {
3161      !!!cp (1001);            !!!cp (1019);
3162      ## Don't consume            $self->{state} = $self->{prev_state};
3163      ## No error            ## Reconsume.
3164      return undef;            !!!emit ({type => CHARACTER_TOKEN,
3165    } elsif ($self->{next_char} == 0x0023) { # #                      data => '&#',
3166      !!!next-input-character;                      line => $self->{line_prev},
3167      if ($self->{next_char} == 0x0078 or # x                      column => $self->{column_prev} - 1,
3168          $self->{next_char} == 0x0058) { # X                     });
3169        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;  
3170          } else {          } else {
3171            !!!cp (1007);            !!!cp (993);
3172            !!!parse-error (type => 'no refc', line => $l, column => $c);            $self->{ca}->{value} .= '&#';
3173              $self->{state} = $self->{prev_state};
3174              ## Reconsume.
3175              redo A;
3176          }          }
3177          }
3178          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {      } elsif ($self->{state} == NCR_NUM_STATE) {
3179            !!!cp (1008);        if (0x0030 <= $self->{nc} and
3180            !!!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  
3181          !!!cp (1012);          !!!cp (1012);
3182          $code *= 10;          $self->{s_kwd} *= 10;
3183          $code += $self->{next_char} - 0x0030;          $self->{s_kwd} += $self->{nc} - 0x0030;
3184                    
3185            ## Stay in the state.
3186          !!!next-input-character;          !!!next-input-character;
3187        }          redo A;
3188          } elsif ($self->{nc} == 0x003B) { # ;
       if ($self->{next_char} == 0x003B) { # ;  
3189          !!!cp (1013);          !!!cp (1013);
3190          !!!next-input-character;          !!!next-input-character;
3191            #
3192        } else {        } else {
3193          !!!cp (1014);          !!!cp (1014);
3194          !!!parse-error (type => 'no refc', line => $l, column => $c);          !!!parse-error (type => 'no refc');
3195            ## Reconsume.
3196            #
3197        }        }
3198    
3199        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3200          my $l = $self->{line_prev};
3201          my $c = $self->{column_prev};
3202          if ($charref_map->{$code}) {
3203          !!!cp (1015);          !!!cp (1015);
3204          !!!parse-error (type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c);          !!!parse-error (type => 'invalid character reference',
3205          $code = 0xFFFD;                          text => (sprintf 'U+%04X', $code),
3206                            line => $l, column => $c);
3207            $code = $charref_map->{$code};
3208        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3209          !!!cp (1016);          !!!cp (1016);
3210          !!!parse-error (type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c);          !!!parse-error (type => 'invalid character reference',
3211                            text => (sprintf 'U-%08X', $code),
3212                            line => $l, column => $c);
3213          $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};  
3214        }        }
3215          
3216        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1,        if ($self->{prev_state} == DATA_STATE) {
3217                line => $l, column => $c};          !!!cp (992);
3218      } else {          $self->{state} = $self->{prev_state};
3219        !!!cp (1019);          ## Reconsume.
3220        !!!parse-error (type => 'bare nero', line => $l, column => $c);          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3221        !!!back-next-input-character ($self->{next_char});                    line => $l, column => $c,
3222        $self->{next_char} = 0x0023; # #                   });
3223        return undef;          redo A;
3224      }        } else {
3225    } elsif ((0x0041 <= $self->{next_char} and          !!!cp (991);
3226              $self->{next_char} <= 0x005A) or          $self->{ca}->{value} .= chr $code;
3227             (0x0061 <= $self->{next_char} and          $self->{ca}->{has_reference} = 1;
3228              $self->{next_char} <= 0x007A)) {          $self->{state} = $self->{prev_state};
3229      my $entity_name = chr $self->{next_char};          ## Reconsume.
3230      !!!next-input-character;          redo A;
3231          }
3232      my $value = $entity_name;      } elsif ($self->{state} == HEXREF_X_STATE) {
3233      my $match = 0;        if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3234      require Whatpm::_NamedEntityList;            (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3235      our $EntityChar;            (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3236            # 0..9, A..F, a..f
3237      while (length $entity_name < 10 and          !!!cp (990);
3238             ## NOTE: Some number greater than the maximum length of entity name          $self->{state} = HEXREF_HEX_STATE;
3239             ((0x0041 <= $self->{next_char} and # a          $self->{s_kwd} = 0;
3240               $self->{next_char} <= 0x005A) or # x          ## Reconsume.
3241              (0x0061 <= $self->{next_char} and # a          redo A;
3242               $self->{next_char} <= 0x007A) or # z        } else {
3243              (0x0030 <= $self->{next_char} and # 0          !!!parse-error (type => 'bare hcro',
3244               $self->{next_char} <= 0x0039) or # 9                          line => $self->{line_prev},
3245              $self->{next_char} == 0x003B)) { # ;                          column => $self->{column_prev} - 2);
3246        $entity_name .= chr $self->{next_char};  
3247        if (defined $EntityChar->{$entity_name}) {          ## NOTE: According to the spec algorithm, nothing is returned,
3248          if ($self->{next_char} == 0x003B) { # ;          ## and then "&#" followed by "X" or "x" is appended to the parent
3249            !!!cp (1020);          ## element or the attribute value in the later processing.
3250            $value = $EntityChar->{$entity_name};  
3251            $match = 1;          if ($self->{prev_state} == DATA_STATE) {
3252            !!!next-input-character;            !!!cp (1005);
3253            last;            $self->{state} = $self->{prev_state};
3254              ## Reconsume.
3255              !!!emit ({type => CHARACTER_TOKEN,
3256                        data => '&' . $self->{s_kwd},
3257                        line => $self->{line_prev},
3258                        column => $self->{column_prev} - length $self->{s_kwd},
3259                       });
3260              redo A;
3261          } else {          } else {
3262            !!!cp (1021);            !!!cp (989);
3263            $value = $EntityChar->{$entity_name};            $self->{ca}->{value} .= '&' . $self->{s_kwd};
3264            $match = -1;            $self->{state} = $self->{prev_state};
3265              ## Reconsume.
3266              redo A;
3267            }
3268          }
3269        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3270          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3271            # 0..9
3272            !!!cp (1002);
3273            $self->{s_kwd} *= 0x10;
3274            $self->{s_kwd} += $self->{nc} - 0x0030;
3275            ## Stay in the state.
3276            !!!next-input-character;
3277            redo A;
3278          } elsif (0x0061 <= $self->{nc} and
3279                   $self->{nc} <= 0x0066) { # a..f
3280            !!!cp (1003);
3281            $self->{s_kwd} *= 0x10;
3282            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3283            ## Stay in the state.
3284            !!!next-input-character;
3285            redo A;
3286          } elsif (0x0041 <= $self->{nc} and
3287                   $self->{nc} <= 0x0046) { # A..F
3288            !!!cp (1004);
3289            $self->{s_kwd} *= 0x10;
3290            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3291            ## Stay in the state.
3292            !!!next-input-character;
3293            redo A;
3294          } elsif ($self->{nc} == 0x003B) { # ;
3295            !!!cp (1006);
3296            !!!next-input-character;
3297            #
3298          } else {
3299            !!!cp (1007);
3300            !!!parse-error (type => 'no refc',
3301                            line => $self->{line},
3302                            column => $self->{column});
3303            ## Reconsume.
3304            #
3305          }
3306    
3307          my $code = $self->{s_kwd};
3308          my $l = $self->{line_prev};
3309          my $c = $self->{column_prev};
3310          if ($charref_map->{$code}) {
3311            !!!cp (1008);
3312            !!!parse-error (type => 'invalid character reference',
3313                            text => (sprintf 'U+%04X', $code),
3314                            line => $l, column => $c);
3315            $code = $charref_map->{$code};
3316          } elsif ($code > 0x10FFFF) {
3317            !!!cp (1009);
3318            !!!parse-error (type => 'invalid character reference',
3319                            text => (sprintf 'U-%08X', $code),
3320                            line => $l, column => $c);
3321            $code = 0xFFFD;
3322          }
3323    
3324          if ($self->{prev_state} == DATA_STATE) {
3325            !!!cp (988);
3326            $self->{state} = $self->{prev_state};
3327            ## Reconsume.
3328            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3329                      line => $l, column => $c,
3330                     });
3331            redo A;
3332          } else {
3333            !!!cp (987);
3334            $self->{ca}->{value} .= chr $code;
3335            $self->{ca}->{has_reference} = 1;
3336            $self->{state} = $self->{prev_state};
3337            ## Reconsume.
3338            redo A;
3339          }
3340        } elsif ($self->{state} == ENTITY_NAME_STATE) {
3341          if (length $self->{s_kwd} < 30 and
3342              ## NOTE: Some number greater than the maximum length of entity name
3343              ((0x0041 <= $self->{nc} and # a
3344                $self->{nc} <= 0x005A) or # x
3345               (0x0061 <= $self->{nc} and # a
3346                $self->{nc} <= 0x007A) or # z
3347               (0x0030 <= $self->{nc} and # 0
3348                $self->{nc} <= 0x0039) or # 9
3349               $self->{nc} == 0x003B)) { # ;
3350            our $EntityChar;
3351            $self->{s_kwd} .= chr $self->{nc};
3352            if (defined $EntityChar->{$self->{s_kwd}}) {
3353              if ($self->{nc} == 0x003B) { # ;
3354                !!!cp (1020);
3355                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3356                $self->{entity__match} = 1;
3357                !!!next-input-character;
3358                #
3359              } else {
3360                !!!cp (1021);
3361                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3362                $self->{entity__match} = -1;
3363                ## Stay in the state.
3364                !!!next-input-character;
3365                redo A;
3366              }
3367            } else {
3368              !!!cp (1022);
3369              $self->{entity__value} .= chr $self->{nc};
3370              $self->{entity__match} *= 2;
3371              ## Stay in the state.
3372            !!!next-input-character;            !!!next-input-character;
3373              redo A;
3374            }
3375          }
3376    
3377          my $data;
3378          my $has_ref;
3379          if ($self->{entity__match} > 0) {
3380            !!!cp (1023);
3381            $data = $self->{entity__value};
3382            $has_ref = 1;
3383            #
3384          } elsif ($self->{entity__match} < 0) {
3385            !!!parse-error (type => 'no refc');
3386            if ($self->{prev_state} != DATA_STATE and # in attribute
3387                $self->{entity__match} < -1) {
3388              !!!cp (1024);
3389              $data = '&' . $self->{s_kwd};
3390              #
3391            } else {
3392              !!!cp (1025);
3393              $data = $self->{entity__value};
3394              $has_ref = 1;
3395              #
3396          }          }
3397        } else {        } else {
3398          !!!cp (1022);          !!!cp (1026);
3399          $value .= chr $self->{next_char};          !!!parse-error (type => 'bare ero',
3400          $match *= 2;                          line => $self->{line_prev},
3401          !!!next-input-character;                          column => $self->{column_prev} - length $self->{s_kwd});
3402            $data = '&' . $self->{s_kwd};
3403            #
3404        }        }
3405      }    
3406              ## NOTE: In these cases, when a character reference is found,
3407      if ($match > 0) {        ## it is consumed and a character token is returned, or, otherwise,
3408        !!!cp (1023);        ## nothing is consumed and returned, according to the spec algorithm.
3409        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,        ## In this implementation, anything that has been examined by the
3410                line => $l, column => $c};        ## tokenizer is appended to the parent element or the attribute value
3411      } elsif ($match < 0) {        ## as string, either literal string when no character reference or
3412        !!!parse-error (type => 'no refc', line => $l, column => $c);        ## entity-replaced string otherwise, in this stage, since any characters
3413        if ($in_attr and $match < -1) {        ## that would not be consumed are appended in the data state or in an
3414          !!!cp (1024);        ## appropriate attribute value state anyway.
3415          return {type => CHARACTER_TOKEN, data => '&'.$entity_name,  
3416                  line => $l, column => $c};        if ($self->{prev_state} == DATA_STATE) {
3417        } else {          !!!cp (986);
3418          !!!cp (1025);          $self->{state} = $self->{prev_state};
3419          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,          ## Reconsume.
3420                  line => $l, column => $c};          !!!emit ({type => CHARACTER_TOKEN,
3421                      data => $data,
3422                      line => $self->{line_prev},
3423                      column => $self->{column_prev} + 1 - length $self->{s_kwd},
3424                     });
3425            redo A;
3426          } else {
3427            !!!cp (985);
3428            $self->{ca}->{value} .= $data;
3429            $self->{ca}->{has_reference} = 1 if $has_ref;
3430            $self->{state} = $self->{prev_state};
3431            ## Reconsume.
3432            redo A;
3433        }        }
3434      } else {      } else {
3435        !!!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};  
3436      }      }
3437    } else {    } # A  
3438      !!!cp (1027);  
3439      ## no characters are consumed    die "$0: _get_next_token: unexpected case";
3440      !!!parse-error (type => 'bare ero', line => $l, column => $c);  } # _get_next_token
     return undef;  
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3441    
3442  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3443    my $self = shift;    my $self = shift;
# Line 2463  sub _initialize_tree_constructor ($) { Line 3446  sub _initialize_tree_constructor ($) {
3446    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3447    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3448    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3449      $self->{document}->set_user_data (manakai_source_line => 1);
3450      $self->{document}->set_user_data (manakai_source_column => 1);
3451  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3452    
3453  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 2517  sub _tree_construction_initial ($) { Line 3502  sub _tree_construction_initial ($) {
3502        ## language.        ## language.
3503        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
3504        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3505        $doctype_name =~ tr/a-z/A-Z/;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3506        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3507            defined $token->{public_identifier} or            defined $token->{sysid}) {
           defined $token->{system_identifier}) {  
3508          !!!cp ('t1');          !!!cp ('t1');
3509          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
3510        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3511          !!!cp ('t2');          !!!cp ('t2');
         ## ISSUE: ASCII case-insensitive? (in fact it does not matter)  
3512          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
3513          } elsif (defined $token->{pubid}) {
3514            if ($token->{pubid} eq 'XSLT-compat') {
3515              !!!cp ('t1.2');
3516              !!!parse-error (type => 'XSLT-compat', token => $token,
3517                              level => $self->{level}->{should});
3518            } else {
3519              !!!parse-error (type => 'not HTML5', token => $token);
3520            }
3521        } else {        } else {
3522          !!!cp ('t3');          !!!cp ('t3');
3523            #
3524        }        }
3525                
3526        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
3527          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3528        $doctype->public_id ($token->{public_identifier})        ## NOTE: Default value for both |public_id| and |system_id| attributes
3529            if defined $token->{public_identifier};        ## are empty strings, so that we don't set any value in missing cases.
3530        $doctype->system_id ($token->{system_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3531            if defined $token->{system_identifier};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
3532        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3533        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3534        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
# Line 2544  sub _tree_construction_initial ($) { Line 3536  sub _tree_construction_initial ($) {
3536        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3537          !!!cp ('t4');          !!!cp ('t4');
3538          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3539        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3540          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3541          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3542          if ({          my $prefix = [
3543            "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3544            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3545            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3546            "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 1//",
3547            "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 2//",
3548            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3549            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3550            "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT//",
3551            "-//IETF//DTD HTML 2.0//EN" => 1,            "-//IETF//DTD HTML 2.0//",
3552            "-//IETF//DTD HTML 2.1E//EN" => 1,            "-//IETF//DTD HTML 2.1E//",
3553            "-//IETF//DTD HTML 3.0//EN" => 1,            "-//IETF//DTD HTML 3.0//",
3554            "-//IETF//DTD HTML 3.0//EN//" => 1,            "-//IETF//DTD HTML 3.2 FINAL//",
3555            "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,            "-//IETF//DTD HTML 3.2//",
3556            "-//IETF//DTD HTML 3.2//EN" => 1,            "-//IETF//DTD HTML 3//",
3557            "-//IETF//DTD HTML 3//EN" => 1,            "-//IETF//DTD HTML LEVEL 0//",
3558            "-//IETF//DTD HTML LEVEL 0//EN" => 1,            "-//IETF//DTD HTML LEVEL 1//",
3559            "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,            "-//IETF//DTD HTML LEVEL 2//",
3560            "-//IETF//DTD HTML LEVEL 1//EN" => 1,            "-//IETF//DTD HTML LEVEL 3//",
3561            "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 0//",
3562            "-//IETF//DTD HTML LEVEL 2//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 1//",
3563            "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 2//",
3564            "-//IETF//DTD HTML LEVEL 3//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 3//",
3565            "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,            "-//IETF//DTD HTML STRICT//",
3566            "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,            "-//IETF//DTD HTML//",
3567            "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,            "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3568            "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3569            "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3570            "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3571            "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3572            "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3573            "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3574            "-//IETF//DTD HTML STRICT//EN" => 1,            "-//NETSCAPE COMM. CORP.//DTD HTML//",
3575            "-//IETF//DTD HTML STRICT//EN//2.0" => 1,            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3576            "-//IETF//DTD HTML STRICT//EN//3.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3577            "-//IETF//DTD HTML//EN" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3578            "-//IETF//DTD HTML//EN//2.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3579            "-//IETF//DTD HTML//EN//3.0" => 1,            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3580            "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3581            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3582            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3583            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3584            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3585            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,            "-//W3C//DTD HTML 3 1995-03-24//",
3586            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,            "-//W3C//DTD HTML 3.2 DRAFT//",
3587            "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,            "-//W3C//DTD HTML 3.2 FINAL//",
3588            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,            "-//W3C//DTD HTML 3.2//",
3589            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,            "-//W3C//DTD HTML 3.2S DRAFT//",
3590            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 FRAMESET//",
3591            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3592            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3593            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3594            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,            "-//W3C//DTD W3 HTML//",
3595            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,            "-//W3O//DTD W3 HTML 3.0//",
3596            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3597            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML//",
3598            "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,          ]; # $prefix
3599            "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,          my $match;
3600            "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,          for (@$prefix) {
3601            "-//W3C//DTD HTML 3.2//EN" => 1,            if (substr ($prefix, 0, length $_) eq $_) {
3602            "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,              $match = 1;
3603            "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,              last;
3604            "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,            }
3605            "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,          }
3606            "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,          if ($match or
3607            "-//W3C//DTD W3 HTML//EN" => 1,              $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3608            "-//W3O//DTD W3 HTML 3.0//EN" => 1,              $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3609            "-//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}) {  
3610            !!!cp ('t5');            !!!cp ('t5');
3611            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3612          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3613                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3614            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3615              !!!cp ('t6');              !!!cp ('t6');
3616              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3617            } else {            } else {
3618              !!!cp ('t7');              !!!cp ('t7');
3619              $self->{document}->manakai_compat_mode ('limited quirks');              $self->{document}->manakai_compat_mode ('limited quirks');
3620            }            }
3621          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3622                   $pubid eq "-//W3C//DTD XHTML 1.0 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3623            !!!cp ('t8');            !!!cp ('t8');
3624            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3625          } else {          } else {
# Line 2642  sub _tree_construction_initial ($) { Line 3628  sub _tree_construction_initial ($) {
3628        } else {        } else {
3629          !!!cp ('t10');          !!!cp ('t10');
3630        }        }
3631        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3632          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3633          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3634          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
3635            ## TODO: Check the spec: PUBLIC "(limited quirks)" "(quirks)"            ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3636              ## marked as quirks.
3637            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3638            !!!cp ('t11');            !!!cp ('t11');
3639          } else {          } else {
# Line 2669  sub _tree_construction_initial ($) { Line 3656  sub _tree_construction_initial ($) {
3656        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3657        ## Go to the "before html" insertion mode.        ## Go to the "before html" insertion mode.
3658        ## reprocess        ## reprocess
3659          !!!ack-later;
3660        return;        return;
3661      } elsif ($token->{type} == CHARACTER_TOKEN) {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3662        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3663          ## Ignore the token          ## Ignore the token
3664    
3665          unless (length $token->{data}) {          unless (length $token->{data}) {
# Line 2728  sub _tree_construction_root_element ($) Line 3716  sub _tree_construction_root_element ($)
3716          !!!next-token;          !!!next-token;
3717          redo B;          redo B;
3718        } elsif ($token->{type} == CHARACTER_TOKEN) {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3719          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3720            ## Ignore the token.            ## Ignore the token.
3721    
3722            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 2749  sub _tree_construction_root_element ($) Line 3737  sub _tree_construction_root_element ($)
3737        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3738          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
3739            my $root_element;            my $root_element;
3740            !!!create-element ($root_element, $token->{tag_name}, $token->{attributes}, $token);            !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3741            $self->{document}->append_child ($root_element);            $self->{document}->append_child ($root_element);
3742            push @{$self->{open_elements}}, [$root_element, 'html'];            push @{$self->{open_elements}},
3743                  [$root_element, $el_category->{html}];
3744    
3745            if ($token->{attributes}->{manifest}) {            if ($token->{attributes}->{manifest}) {
3746              !!!cp ('t24');              !!!cp ('t24');
3747              $self->{application_cache_selection}              $self->{application_cache_selection}
3748                  ->($token->{attributes}->{manifest}->{value});                  ->($token->{attributes}->{manifest}->{value});
3749              ## ISSUE: No relative reference resolution?              ## ISSUE: Spec is unclear on relative references.
3750                ## According to Hixie (#whatwg 2008-03-19), it should be
3751                ## resolved against the base URI of the document in HTML
3752                ## or xml:base of the element in XHTML.
3753            } else {            } else {
3754              !!!cp ('t25');              !!!cp ('t25');
3755              $self->{application_cache_selection}->(undef);              $self->{application_cache_selection}->(undef);
3756            }            }
3757    
3758              !!!nack ('t25c');
3759    
3760            !!!next-token;            !!!next-token;
3761            return; ## Go to the "before head" insertion mode.            return; ## Go to the "before head" insertion mode.
3762          } else {          } else {
# Line 2779  sub _tree_construction_root_element ($) Line 3773  sub _tree_construction_root_element ($)
3773          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3774        }        }
3775    
3776      my $root_element; !!!create-element ($root_element, 'html',, $token);      my $root_element;
3777        !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3778      $self->{document}->append_child ($root_element);      $self->{document}->append_child ($root_element);
3779      push @{$self->{open_elements}}, [$root_element, 'html'];      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3780    
3781      $self->{application_cache_selection}->(undef);      $self->{application_cache_selection}->(undef);
3782    
3783      ## NOTE: Reprocess the token.      ## NOTE: Reprocess the token.
3784        !!!ack-later;
3785      return; ## Go to the "before head" insertion mode.      return; ## Go to the "before head" insertion mode.
3786    
3787      ## ISSUE: There is an issue in the spec      ## ISSUE: There is an issue in the spec
# Line 2809  sub _reset_insertion_mode ($) { Line 3805  sub _reset_insertion_mode ($) {
3805        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3806          $last = 1;          $last = 1;
3807          if (defined $self->{inner_html_node}) {          if (defined $self->{inner_html_node}) {
3808            if ($self->{inner_html_node}->[1] eq 'td' or            !!!cp ('t28');
3809                $self->{inner_html_node}->[1] eq 'th') {            $node = $self->{inner_html_node};
3810              !!!cp ('t27');          } else {
3811              #            die "_reset_insertion_mode: t27";
           } else {  
             !!!cp ('t28');  
             $node = $self->{inner_html_node};  
           }  
3812          }          }
3813        }        }
3814              
3815        ## Step 4..13        ## Step 4..14
3816        my $new_mode = {        my $new_mode;
3817          if ($node->[1] & FOREIGN_EL) {
3818            !!!cp ('t28.1');
3819            ## NOTE: Strictly spaking, the line below only applies to MathML and
3820            ## SVG elements.  Currently the HTML syntax supports only MathML and
3821            ## SVG elements as foreigners.
3822            $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3823          } elsif ($node->[1] & TABLE_CELL_EL) {
3824            if ($last) {
3825              !!!cp ('t28.2');
3826              #
3827            } else {
3828              !!!cp ('t28.3');
3829              $new_mode = IN_CELL_IM;
3830            }
3831          } else {
3832            !!!cp ('t28.4');
3833            $new_mode = {
3834                        select => IN_SELECT_IM,                        select => IN_SELECT_IM,
3835                        ## NOTE: |option| and |optgroup| do not set                        ## NOTE: |option| and |optgroup| do not set
3836                        ## insertion mode to "in select" by themselves.                        ## insertion mode to "in select" by themselves.
                       td => IN_CELL_IM,  
                       th => IN_CELL_IM,  
3837                        tr => IN_ROW_IM,                        tr => IN_ROW_IM,
3838                        tbody => IN_TABLE_BODY_IM,                        tbody => IN_TABLE_BODY_IM,
3839                        thead => IN_TABLE_BODY_IM,                        thead => IN_TABLE_BODY_IM,
# Line 2837  sub _reset_insertion_mode ($) { Line 3844  sub _reset_insertion_mode ($) {
3844                        head => IN_BODY_IM, # not in head!                        head => IN_BODY_IM, # not in head!
3845                        body => IN_BODY_IM,                        body => IN_BODY_IM,
3846                        frameset => IN_FRAMESET_IM,                        frameset => IN_FRAMESET_IM,
3847                       }->{$node->[1]};                       }->{$node->[0]->manakai_local_name};
3848          }
3849        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3850                
3851        ## Step 14        ## Step 15
3852        if ($node->[1] eq 'html') {        if ($node->[1] & HTML_EL) {
3853          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3854            !!!cp ('t29');            !!!cp ('t29');
3855            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
# Line 2855  sub _reset_insertion_mode ($) { Line 3863  sub _reset_insertion_mode ($) {
3863          !!!cp ('t31');          !!!cp ('t31');
3864        }        }
3865                
3866        ## Step 15        ## Step 16
3867        $self->{insertion_mode} = IN_BODY_IM and return if $last;        $self->{insertion_mode} = IN_BODY_IM and return if $last;
3868                
3869        ## Step 16        ## Step 17
3870        $i--;        $i--;
3871        $node = $self->{open_elements}->[$i];        $node = $self->{open_elements}->[$i];
3872                
3873        ## Step 17        ## Step 18
3874        redo S3;        redo S3;
3875      } # S3      } # S3
3876    
# Line 2974  sub _tree_construction_main ($) { Line 3982  sub _tree_construction_main ($) {
3982      ## Step 1      ## Step 1
3983      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
3984      my $el;      my $el;
3985      !!!create-element ($el, $start_tag_name, $token->{attributes}, $token);      !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);
3986    
3987      ## Step 2      ## Step 2
3988      $insert->($el);      $insert->($el);
# Line 2985  sub _tree_construction_main ($) { Line 3993  sub _tree_construction_main ($) {
3993    
3994      ## Step 4      ## Step 4
3995      my $text = '';      my $text = '';
3996        !!!nack ('t40.1');
3997      !!!next-token;      !!!next-token;
3998      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
3999        !!!cp ('t40');        !!!cp ('t40');
# Line 3011  sub _tree_construction_main ($) { Line 4020  sub _tree_construction_main ($) {
4020        ## NOTE: An end-of-file token.        ## NOTE: An end-of-file token.
4021        if ($content_model_flag == CDATA_CONTENT_MODEL) {        if ($content_model_flag == CDATA_CONTENT_MODEL) {
4022          !!!cp ('t43');          !!!cp ('t43');
4023          !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token);          !!!parse-error (type => 'in CDATA:#eof', token => $token);
4024        } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {        } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
4025          !!!cp ('t44');          !!!cp ('t44');
4026          !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token);          !!!parse-error (type => 'in RCDATA:#eof', token => $token);
4027        } else {        } else {
4028          die "$0: $content_model_flag in parse_rcdata";          die "$0: $content_model_flag in parse_rcdata";
4029        }        }
# Line 3024  sub _tree_construction_main ($) { Line 4033  sub _tree_construction_main ($) {
4033    
4034    my $script_start_tag = sub () {    my $script_start_tag = sub () {
4035      my $script_el;      my $script_el;
4036      !!!create-element ($script_el, 'script', $token->{attributes}, $token);      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
4037      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
4038    
4039      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
4040      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
4041            
4042      my $text = '';      my $text = '';
4043        !!!nack ('t45.1');
4044      !!!next-token;      !!!next-token;
4045      while ($token->{type} == CHARACTER_TOKEN) {      while ($token->{type} == CHARACTER_TOKEN) {
4046        !!!cp ('t45');        !!!cp ('t45');
# Line 3050  sub _tree_construction_main ($) { Line 4060  sub _tree_construction_main ($) {
4060        ## Ignore the token        ## Ignore the token
4061      } else {      } else {
4062        !!!cp ('t48');        !!!cp ('t48');
4063        !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token);        !!!parse-error (type => 'in CDATA:#eof', token => $token);
4064        ## ISSUE: And ignore?        ## ISSUE: And ignore?
4065        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4066      }      }
# Line 3088  sub _tree_construction_main ($) { Line 4098  sub _tree_construction_main ($) {
4098        my $formatting_element;        my $formatting_element;
4099        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4100        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4101          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4102              !!!cp ('t52');
4103              last AFE;
4104            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4105                         eq $tag_name) {
4106            !!!cp ('t51');            !!!cp ('t51');
4107            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
4108            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
4109            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           !!!cp ('t52');  
           last AFE;  
4110          }          }
4111        } # AFE        } # AFE
4112        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4113          !!!cp ('t53');          !!!cp ('t53');
4114          !!!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);
4115          ## Ignore the token          ## Ignore the token
4116          !!!next-token;          !!!next-token;
4117          return;          return;
# Line 3117  sub _tree_construction_main ($) { Line 4128  sub _tree_construction_main ($) {
4128              last INSCOPE;              last INSCOPE;
4129            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4130              !!!cp ('t55');              !!!cp ('t55');
4131              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},              !!!parse-error (type => 'unmatched end tag',
4132                                text => $token->{tag_name},
4133                              token => $end_tag_token);                              token => $end_tag_token);
4134              ## Ignore the token              ## Ignore the token
4135              !!!next-token;              !!!next-token;
4136              return;              return;
4137            }            }
4138          } 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]}) {  
4139            !!!cp ('t56');            !!!cp ('t56');
4140            $in_scope = 0;            $in_scope = 0;
4141          }          }
4142        } # INSCOPE        } # INSCOPE
4143        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4144          !!!cp ('t57');          !!!cp ('t57');
4145          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},          !!!parse-error (type => 'unmatched end tag',
4146                            text => $token->{tag_name},
4147                          token => $end_tag_token);                          token => $end_tag_token);
4148          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4149          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
# Line 3141  sub _tree_construction_main ($) { Line 4151  sub _tree_construction_main ($) {
4151        }        }
4152        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4153          !!!cp ('t58');          !!!cp ('t58');
4154          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1],          !!!parse-error (type => 'not closed',
4155                            text => $self->{open_elements}->[-1]->[0]
4156                                ->manakai_local_name,
4157                          token => $end_tag_token);                          token => $end_tag_token);
4158        }        }
4159                
# Line 3150  sub _tree_construction_main ($) { Line 4162  sub _tree_construction_main ($) {
4162        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4163        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4164          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4165          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4166              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4167              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4168               $scoping_category->{$node->[1]})) { ## Scoping is redundant, maybe               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4169            !!!cp ('t59');            !!!cp ('t59');
4170            $furthest_block = $node;            $furthest_block = $node;
4171            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
# Line 3239  sub _tree_construction_main ($) { Line 4251  sub _tree_construction_main ($) {
4251        } # S7          } # S7  
4252                
4253        ## Step 8        ## Step 8
4254        if ({        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
            table => 1, tbody => 1, tfoot => 1, thead => 1, tr => 1,  
           }->{$common_ancestor_node->[1]}) {  
4255          my $foster_parent_element;          my $foster_parent_element;
4256          my $next_sibling;          my $next_sibling;
4257                           OE: for (reverse 0..$#{$self->{open_elements}}) {          OE: for (reverse 0..$#{$self->{open_elements}}) {
4258                             if ($self->{open_elements}->[$_]->[1] eq 'table') {            if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4259                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4260                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4261                                 !!!cp ('t65.1');                                 !!!cp ('t65.1');
# Line 3318  sub _tree_construction_main ($) { Line 4328  sub _tree_construction_main ($) {
4328    
4329    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4330      my $child = shift;      my $child = shift;
4331      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]}) {  
4332        # MUST        # MUST
4333        my $foster_parent_element;        my $foster_parent_element;
4334        my $next_sibling;        my $next_sibling;
4335                           OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4336                             if ($self->{open_elements}->[$_]->[1] eq 'table') {          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4337                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4338                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4339                                 !!!cp ('t70');                                 !!!cp ('t70');
# Line 3350  sub _tree_construction_main ($) { Line 4358  sub _tree_construction_main ($) {
4358      }      }
4359    }; # $insert_to_foster    }; # $insert_to_foster
4360    
4361    B: {    B: while (1) {
4362      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
4363        !!!cp ('t73');        !!!cp ('t73');
4364        !!!parse-error (type => 'DOCTYPE in the middle', token => $token);        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4365        ## Ignore the token        ## Ignore the token
4366        ## Stay in the phase        ## Stay in the phase
4367        !!!next-token;        !!!next-token;
4368        redo B;        next B;
4369      } elsif ($token->{type} == START_TAG_TOKEN and      } elsif ($token->{type} == START_TAG_TOKEN and
4370               $token->{tag_name} eq 'html') {               $token->{tag_name} eq 'html') {
4371        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4372          !!!cp ('t79');          !!!cp ('t79');
4373          !!!parse-error (type => 'after html:html', token => $token);          !!!parse-error (type => 'after html', text => 'html', token => $token);
4374          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
4375        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4376          !!!cp ('t80');          !!!cp ('t80');
4377          !!!parse-error (type => 'after html:html', token => $token);          !!!parse-error (type => 'after html', text => 'html', token => $token);
4378          $self->{insertion_mode} = AFTER_FRAMESET_IM;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4379        } else {        } else {
4380          !!!cp ('t81');          !!!cp ('t81');
# Line 3383  sub _tree_construction_main ($) { Line 4391  sub _tree_construction_main ($) {
4391               $token->{attributes}->{$attr_name}->{value});               $token->{attributes}->{$attr_name}->{value});
4392          }          }
4393        }        }
4394          !!!nack ('t84.1');
4395        !!!next-token;        !!!next-token;
4396        redo B;        next B;
4397      } elsif ($token->{type} == COMMENT_TOKEN) {      } elsif ($token->{type} == COMMENT_TOKEN) {
4398        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
4399        if ($self->{insertion_mode} & AFTER_HTML_IMS) {        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
# Line 3398  sub _tree_construction_main ($) { Line 4407  sub _tree_construction_main ($) {
4407          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
4408        }        }
4409        !!!next-token;        !!!next-token;
4410        redo B;        next B;
4411      } elsif ($self->{insertion_mode} & HEAD_IMS) {      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4412        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4413          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          !!!cp ('t87.1');
4414            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4415            !!!next-token;
4416            next B;
4417          } elsif ($token->{type} == START_TAG_TOKEN) {
4418            if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4419                 $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4420                not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4421                ($token->{tag_name} eq 'svg' and
4422                 $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
4423              ## NOTE: "using the rules for secondary insertion mode"then"continue"
4424              !!!cp ('t87.2');
4425              #
4426            } elsif ({
4427                      b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4428                      center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4429                      em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4430                      h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4431                      img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4432                      nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4433                      small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4434                      sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4435                     }->{$token->{tag_name}}) {
4436              !!!cp ('t87.2');
4437              !!!parse-error (type => 'not closed',
4438                              text => $self->{open_elements}->[-1]->[0]
4439                                  ->manakai_local_name,
4440                              token => $token);
4441    
4442              pop @{$self->{open_elements}}
4443                  while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4444    
4445              $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4446              ## Reprocess.
4447              next B;
4448            } else {
4449              my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4450              my $tag_name = $token->{tag_name};
4451              if ($nsuri eq $SVG_NS) {
4452                $tag_name = {
4453                   altglyph => 'altGlyph',
4454                   altglyphdef => 'altGlyphDef',
4455                   altglyphitem => 'altGlyphItem',
4456                   animatecolor => 'animateColor',
4457                   animatemotion => 'animateMotion',
4458                   animatetransform => 'animateTransform',
4459                   clippath => 'clipPath',
4460                   feblend => 'feBlend',
4461                   fecolormatrix => 'feColorMatrix',
4462                   fecomponenttransfer => 'feComponentTransfer',
4463                   fecomposite => 'feComposite',
4464                   feconvolvematrix => 'feConvolveMatrix',
4465                   fediffuselighting => 'feDiffuseLighting',
4466                   fedisplacementmap => 'feDisplacementMap',
4467                   fedistantlight => 'feDistantLight',
4468                   feflood => 'feFlood',
4469                   fefunca => 'feFuncA',
4470                   fefuncb => 'feFuncB',
4471                   fefuncg => 'feFuncG',
4472                   fefuncr => 'feFuncR',
4473                   fegaussianblur => 'feGaussianBlur',
4474                   feimage => 'feImage',
4475                   femerge => 'feMerge',
4476                   femergenode => 'feMergeNode',
4477                   femorphology => 'feMorphology',
4478                   feoffset => 'feOffset',
4479                   fepointlight => 'fePointLight',
4480                   fespecularlighting => 'feSpecularLighting',
4481                   fespotlight => 'feSpotLight',
4482                   fetile => 'feTile',
4483                   feturbulence => 'feTurbulence',
4484                   foreignobject => 'foreignObject',
4485                   glyphref => 'glyphRef',
4486                   lineargradient => 'linearGradient',
4487                   radialgradient => 'radialGradient',
4488                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4489                   textpath => 'textPath',  
4490                }->{$tag_name} || $tag_name;
4491              }
4492    
4493              ## "adjust SVG attributes" (SVG only) - done in insert-element-f
4494    
4495              ## "adjust foreign attributes" - done in insert-element-f
4496    
4497              !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
4498    
4499              if ($self->{self_closing}) {
4500                pop @{$self->{open_elements}};
4501                !!!ack ('t87.3');
4502              } else {
4503                !!!cp ('t87.4');
4504              }
4505    
4506              !!!next-token;
4507              next B;
4508            }
4509          } elsif ($token->{type} == END_TAG_TOKEN) {
4510            ## NOTE: "using the rules for secondary insertion mode" then "continue"
4511            !!!cp ('t87.5');
4512            #
4513          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4514            !!!cp ('t87.6');
4515            !!!parse-error (type => 'not closed',
4516                            text => $self->{open_elements}->[-1]->[0]
4517                                ->manakai_local_name,
4518                            token => $token);
4519    
4520            pop @{$self->{open_elements}}
4521                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4522    
4523            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4524            ## Reprocess.
4525            next B;
4526          } else {
4527            die "$0: $token->{type}: Unknown token type";        
4528          }
4529        }
4530    
4531        if ($self->{insertion_mode} & HEAD_IMS) {
4532          if ($token->{type} == CHARACTER_TOKEN) {
4533            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4534            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4535              !!!cp ('t88.2');              !!!cp ('t88.2');
4536              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4537                #
4538            } else {            } else {
4539              !!!cp ('t88.1');              !!!cp ('t88.1');
4540              ## Ignore the token.              ## Ignore the token.
4541              !!!next-token;              #
             redo B;  
4542            }            }
4543            unless (length $token->{data}) {            unless (length $token->{data}) {
4544              !!!cp ('t88');              !!!cp ('t88');
4545              !!!next-token;              !!!next-token;
4546              redo B;              next B;
4547            }            }
4548    ## TODO: set $token->{column} appropriately
4549          }          }
4550    
4551          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4552            !!!cp ('t89');            !!!cp ('t89');
4553            ## As if <head>            ## As if <head>
4554            !!!create-element ($self->{head_element}, 'head',, $token);            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4555            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4556            push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            push @{$self->{open_elements}},
4557                  [$self->{head_element}, $el_category->{head}];
4558    
4559            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4560            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
# Line 3433  sub _tree_construction_main ($) { Line 4564  sub _tree_construction_main ($) {
4564            !!!cp ('t90');            !!!cp ('t90');
4565            ## As if </noscript>            ## As if </noscript>
4566            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4567            !!!parse-error (type => 'in noscript:#character', token => $token);            !!!parse-error (type => 'in noscript:#text', token => $token);
4568                        
4569            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4570            ## As if </head>            ## As if </head>
# Line 3449  sub _tree_construction_main ($) { Line 4580  sub _tree_construction_main ($) {
4580            !!!cp ('t92');            !!!cp ('t92');
4581          }          }
4582    
4583              ## "after head" insertion mode          ## "after head" insertion mode
4584              ## As if <body>          ## As if <body>
4585              !!!insert-element ('body',, $token);          !!!insert-element ('body',, $token);
4586              $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
4587              ## reprocess          ## reprocess
4588              redo B;          next B;
4589            } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
4590              if ($token->{tag_name} eq 'head') {          if ($token->{tag_name} eq 'head') {
4591                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4592                  !!!cp ('t93');              !!!cp ('t93');
4593                  !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes}, $token);              !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4594                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              $self->{open_elements}->[-1]->[0]->append_child
4595                  push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}];                  ($self->{head_element});
4596                  $self->{insertion_mode} = IN_HEAD_IM;              push @{$self->{open_elements}},
4597                  !!!next-token;                  [$self->{head_element}, $el_category->{head}];
4598                  redo B;              $self->{insertion_mode} = IN_HEAD_IM;
4599                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              !!!nack ('t93.1');
4600                  !!!cp ('t94');              !!!next-token;
4601                  #              next B;
4602                } else {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4603                  !!!cp ('t95');              !!!cp ('t93.2');
4604                  !!!parse-error (type => 'in head:head', token => $token); # or in head noscript              !!!parse-error (type => 'after head', text => 'head',
4605                  ## Ignore the token                              token => $token);
4606                  !!!next-token;              ## Ignore the token
4607                  redo B;              !!!nack ('t93.3');
4608                }              !!!next-token;
4609              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              next B;
4610                !!!cp ('t96');            } else {
4611                ## As if <head>              !!!cp ('t95');
4612                !!!create-element ($self->{head_element}, 'head',, $token);              !!!parse-error (type => 'in head:head',
4613                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                              token => $token); # or in head noscript
4614                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              ## Ignore the token
4615                !!!nack ('t95.1');
4616                !!!next-token;
4617                next B;
4618              }
4619            } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4620              !!!cp ('t96');
4621              ## As if <head>
4622              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4623              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4624              push @{$self->{open_elements}},
4625                  [$self->{head_element}, $el_category->{head}];
4626    
4627                $self->{insertion_mode} = IN_HEAD_IM;            $self->{insertion_mode} = IN_HEAD_IM;
4628                ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4629              } else {          } else {
4630                !!!cp ('t97');            !!!cp ('t97');
4631              }          }
4632    
4633              if ($token->{tag_name} eq 'base') {              if ($token->{tag_name} eq 'base') {
4634                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4635                  !!!cp ('t98');                  !!!cp ('t98');
4636                  ## As if </noscript>                  ## As if </noscript>
4637                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4638                  !!!parse-error (type => 'in noscript:base', token => $token);                  !!!parse-error (type => 'in noscript', text => 'base',
4639                                    token => $token);
4640                                
4641                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4642                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
# Line 3504  sub _tree_construction_main ($) { Line 4647  sub _tree_construction_main ($) {
4647                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4648                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4649                  !!!cp ('t100');                  !!!cp ('t100');
4650                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4651                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4652                    push @{$self->{open_elements}},
4653                        [$self->{head_element}, $el_category->{head}];
4654                } else {                } else {
4655                  !!!cp ('t101');                  !!!cp ('t101');
4656                }                }
# Line 3513  sub _tree_construction_main ($) { Line 4658  sub _tree_construction_main ($) {
4658                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
4659                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4660                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4661                  !!!nack ('t101.1');
4662                !!!next-token;                !!!next-token;
4663                redo B;                next B;
4664              } elsif ($token->{tag_name} eq 'link') {              } elsif ($token->{tag_name} eq 'link') {
4665                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4666                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4667                  !!!cp ('t102');                  !!!cp ('t102');
4668                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4669                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4670                    push @{$self->{open_elements}},
4671                        [$self->{head_element}, $el_category->{head}];
4672                } else {                } else {
4673                  !!!cp ('t103');                  !!!cp ('t103');
4674                }                }
# Line 3528  sub _tree_construction_main ($) { Line 4676  sub _tree_construction_main ($) {
4676                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
4677                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4678                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4679                  !!!ack ('t103.1');
4680                !!!next-token;                !!!next-token;
4681                redo B;                next B;
4682              } elsif ($token->{tag_name} eq 'meta') {              } elsif ($token->{tag_name} eq 'meta') {
4683                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4684                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4685                  !!!cp ('t104');                  !!!cp ('t104');
4686                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4687                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4688                    push @{$self->{open_elements}},
4689                        [$self->{head_element}, $el_category->{head}];
4690                } else {                } else {
4691                  !!!cp ('t105');                  !!!cp ('t105');
4692                }                }
# Line 3543  sub _tree_construction_main ($) { Line 4694  sub _tree_construction_main ($) {
4694                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.
4695    
4696                unless ($self->{confident}) {                unless ($self->{confident}) {
4697                  if ($token->{attributes}->{charset}) { ## TODO: And if supported                  if ($token->{attributes}->{charset}) {
4698                    !!!cp ('t106');                    !!!cp ('t106');
4699                      ## NOTE: Whether the encoding is supported or not is handled
4700                      ## in the {change_encoding} callback.
4701                    $self->{change_encoding}                    $self->{change_encoding}
4702                        ->($self, $token->{attributes}->{charset}->{value},                        ->($self, $token->{attributes}->{charset}->{value},
4703                           $token);                           $token);
# Line 3554  sub _tree_construction_main ($) { Line 4707  sub _tree_construction_main ($) {
4707                                             $token->{attributes}->{charset}                                             $token->{attributes}->{charset}
4708                                                 ->{has_reference});                                                 ->{has_reference});
4709                  } elsif ($token->{attributes}->{content}) {                  } elsif ($token->{attributes}->{content}) {
                   ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
4710                    if ($token->{attributes}->{content}->{value}                    if ($token->{attributes}->{content}->{value}
4711                        =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4712                            [\x09-\x0D\x20]*=                            [\x09\x0A\x0C\x0D\x20]*=
4713                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4714                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                            ([^"'\x09\x0A\x0C\x0D\x20]
4715                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4716                      !!!cp ('t107');                      !!!cp ('t107');
4717                        ## NOTE: Whether the encoding is supported or not is handled
4718                        ## in the {change_encoding} callback.
4719                      $self->{change_encoding}                      $self->{change_encoding}
4720                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4721                             $token);                             $token);
# Line 3591  sub _tree_construction_main ($) { Line 4746  sub _tree_construction_main ($) {
4746    
4747                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4748                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4749                  !!!ack ('t110.1');
4750                !!!next-token;                !!!next-token;
4751                redo B;                next B;
4752              } elsif ($token->{tag_name} eq 'title') {              } elsif ($token->{tag_name} eq 'title') {
4753                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4754                  !!!cp ('t111');                  !!!cp ('t111');
4755                  ## As if </noscript>                  ## As if </noscript>
4756                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4757                  !!!parse-error (type => 'in noscript:title', token => $token);                  !!!parse-error (type => 'in noscript', text => 'title',
4758                                    token => $token);
4759                                
4760                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4761                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4762                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4763                  !!!cp ('t112');                  !!!cp ('t112');
4764                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4765                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4766                    push @{$self->{open_elements}},
4767                        [$self->{head_element}, $el_category->{head}];
4768                } else {                } else {
4769                  !!!cp ('t113');                  !!!cp ('t113');
4770                }                }
# Line 3616  sub _tree_construction_main ($) { Line 4775  sub _tree_construction_main ($) {
4775                $parse_rcdata->(RCDATA_CONTENT_MODEL);                $parse_rcdata->(RCDATA_CONTENT_MODEL);
4776                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4777                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4778                redo B;                next B;
4779              } elsif ($token->{tag_name} eq 'style') {              } elsif ($token->{tag_name} eq 'style' or
4780                         $token->{tag_name} eq 'noframes') {
4781                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4782                ## insertion mode IN_HEAD_IM)                ## insertion mode IN_HEAD_IM)
4783                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4784                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4785                  !!!cp ('t114');                  !!!cp ('t114');
4786                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4787                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4788                    push @{$self->{open_elements}},
4789                        [$self->{head_element}, $el_category->{head}];
4790                } else {                } else {
4791                  !!!cp ('t115');                  !!!cp ('t115');
4792                }                }
4793                $parse_rcdata->(CDATA_CONTENT_MODEL);                $parse_rcdata->(CDATA_CONTENT_MODEL);
4794                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4795                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4796                redo B;                next B;
4797              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
4798                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
4799                  !!!cp ('t116');                  !!!cp ('t116');
4800                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
4801                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4802                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4803                    !!!nack ('t116.1');
4804                  !!!next-token;                  !!!next-token;
4805                  redo B;                  next B;
4806                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4807                  !!!cp ('t117');                  !!!cp ('t117');
4808                  !!!parse-error (type => 'in noscript:noscript', token => $token);                  !!!parse-error (type => 'in noscript', text => 'noscript',
4809                                    token => $token);
4810                  ## Ignore the token                  ## Ignore the token
4811                    !!!nack ('t117.1');
4812                  !!!next-token;                  !!!next-token;
4813                  redo B;                  next B;
4814                } else {                } else {
4815                  !!!cp ('t118');                  !!!cp ('t118');
4816                  #                  #
# Line 3655  sub _tree_construction_main ($) { Line 4820  sub _tree_construction_main ($) {
4820                  !!!cp ('t119');                  !!!cp ('t119');
4821                  ## As if </noscript>                  ## As if </noscript>
4822                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4823                  !!!parse-error (type => 'in noscript:script', token => $token);                  !!!parse-error (type => 'in noscript', text => 'script',
4824                                    token => $token);
4825                                
4826                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4827                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4828                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4829                  !!!cp ('t120');                  !!!cp ('t120');
4830                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4831                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4832                    push @{$self->{open_elements}},
4833                        [$self->{head_element}, $el_category->{head}];
4834                } else {                } else {
4835                  !!!cp ('t121');                  !!!cp ('t121');
4836                }                }
# Line 3671  sub _tree_construction_main ($) { Line 4839  sub _tree_construction_main ($) {
4839                $script_start_tag->();                $script_start_tag->();
4840                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4841                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4842                redo B;                next B;
4843              } elsif ($token->{tag_name} eq 'body' or              } elsif ($token->{tag_name} eq 'body' or
4844                       $token->{tag_name} eq 'frameset') {                       $token->{tag_name} eq 'frameset') {
4845                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4846                  !!!cp ('t122');                  !!!cp ('t122');
4847                  ## As if </noscript>                  ## As if </noscript>
4848                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4849                  !!!parse-error (type => 'in noscript:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'in noscript',
4850                                    text => $token->{tag_name}, token => $token);
4851                                    
4852                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4853                  ## As if </head>                  ## As if </head>
# Line 3705  sub _tree_construction_main ($) { Line 4874  sub _tree_construction_main ($) {
4874                } else {                } else {
4875                  die "$0: tag name: $self->{tag_name}";                  die "$0: tag name: $self->{tag_name}";
4876                }                }
4877                  !!!nack ('t127.1');
4878                !!!next-token;                !!!next-token;
4879                redo B;                next B;
4880              } else {              } else {
4881                !!!cp ('t128');                !!!cp ('t128');
4882                #                #
# Line 3716  sub _tree_construction_main ($) { Line 4886  sub _tree_construction_main ($) {
4886                !!!cp ('t129');                !!!cp ('t129');
4887                ## As if </noscript>                ## As if </noscript>
4888                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4889                !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'in noscript:/',
4890                                  text => $token->{tag_name}, token => $token);
4891                                
4892                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
4893                ## As if </head>                ## As if </head>
# Line 3738  sub _tree_construction_main ($) { Line 4909  sub _tree_construction_main ($) {
4909              !!!insert-element ('body',, $token);              !!!insert-element ('body',, $token);
4910              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
4911              ## reprocess              ## reprocess
4912              redo B;              !!!ack-later;
4913                next B;
4914            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
4915              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
4916                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4917                  !!!cp ('t132');                  !!!cp ('t132');
4918                  ## As if <head>                  ## As if <head>
4919                  !!!create-element ($self->{head_element}, 'head',, $token);                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4920                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4921                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
4922                        [$self->{head_element}, $el_category->{head}];
4923    
4924                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4925                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4926                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4927                  !!!next-token;                  !!!next-token;
4928                  redo B;                  next B;
4929                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4930                  !!!cp ('t133');                  !!!cp ('t133');
4931                  ## As if </noscript>                  ## As if </noscript>
4932                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4933                  !!!parse-error (type => 'in noscript:/head', token => $token);                  !!!parse-error (type => 'in noscript:/',
4934                                    text => 'head', token => $token);
4935                                    
4936                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4937                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4938                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4939                  !!!next-token;                  !!!next-token;
4940                  redo B;                  next B;
4941                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4942                  !!!cp ('t134');                  !!!cp ('t134');
4943                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4944                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4945                  !!!next-token;                  !!!next-token;
4946                  redo B;                  next B;
4947                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4948                    !!!cp ('t134.1');
4949                    !!!parse-error (type => 'unmatched end tag', text => 'head',
4950                                    token => $token);
4951                    ## Ignore the token
4952                    !!!next-token;
4953                    next B;
4954                } else {                } else {
4955                  !!!cp ('t135');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                 #  
4956                }                }
4957              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
4958                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
# Line 3780  sub _tree_construction_main ($) { Line 4960  sub _tree_construction_main ($) {
4960                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4961                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4962                  !!!next-token;                  !!!next-token;
4963                  redo B;                  next B;
4964                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
4965                           $self->{insertion_mode} == AFTER_HEAD_IM) {
4966                  !!!cp ('t137');                  !!!cp ('t137');
4967                  !!!parse-error (type => 'unmatched end tag:noscript', token => $token);                  !!!parse-error (type => 'unmatched end tag',
4968                                    text => 'noscript', token => $token);
4969                  ## Ignore the token ## ISSUE: An issue in the spec.                  ## Ignore the token ## ISSUE: An issue in the spec.
4970                  !!!next-token;                  !!!next-token;
4971                  redo B;                  next B;
4972                } else {                } else {
4973                  !!!cp ('t138');                  !!!cp ('t138');
4974                  #                  #
# Line 3794  sub _tree_construction_main ($) { Line 4976  sub _tree_construction_main ($) {
4976              } elsif ({              } elsif ({
4977                        body => 1, html => 1,                        body => 1, html => 1,
4978                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
4979                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
4980                  !!!cp ('t139');                    $self->{insertion_mode} == IN_HEAD_IM or
4981                  ## As if <head>                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
                 !!!create-element ($self->{head_element}, 'head',, $token);  
                 $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) {  
4982                  !!!cp ('t140');                  !!!cp ('t140');
4983                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
4984                                    text => $token->{tag_name}, token => $token);
4985                    ## Ignore the token
4986                    !!!next-token;
4987                    next B;
4988                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4989                    !!!cp ('t140.1');
4990                    !!!parse-error (type => 'unmatched end tag',
4991                                    text => $token->{tag_name}, token => $token);
4992                  ## Ignore the token                  ## Ignore the token
4993                  !!!next-token;                  !!!next-token;
4994                  redo B;                  next B;
4995                } else {                } else {
4996                  !!!cp ('t141');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
4997                }                }
4998                              } elsif ($token->{tag_name} eq 'p') {
4999                #                !!!cp ('t142');
5000              } elsif ({                !!!parse-error (type => 'unmatched end tag',
5001                        p => 1, br => 1,                                text => $token->{tag_name}, token => $token);
5002                       }->{$token->{tag_name}}) {                ## Ignore the token
5003                  !!!next-token;
5004                  next B;
5005                } elsif ($token->{tag_name} eq 'br') {
5006                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5007                  !!!cp ('t142');                  !!!cp ('t142.2');
5008                  ## As if <head>                  ## (before head) as if <head>, (in head) as if </head>
5009                  !!!create-element ($self->{head_element}, 'head',, $token);                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5010                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5011                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  $self->{insertion_mode} = AFTER_HEAD_IM;
5012      
5013                    ## Reprocess in the "after head" insertion mode...
5014                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5015                    !!!cp ('t143.2');
5016                    ## As if </head>
5017                    pop @{$self->{open_elements}};
5018                    $self->{insertion_mode} = AFTER_HEAD_IM;
5019      
5020                    ## Reprocess in the "after head" insertion mode...
5021                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5022                    !!!cp ('t143.3');
5023                    ## ISSUE: Two parse errors for <head><noscript></br>
5024                    !!!parse-error (type => 'unmatched end tag',
5025                                    text => 'br', token => $token);
5026                    ## As if </noscript>
5027                    pop @{$self->{open_elements}};
5028                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5029    
5030                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5031                } else {                  ## As if </head>
5032                  !!!cp ('t143');                  pop @{$self->{open_elements}};
5033                }                  $self->{insertion_mode} = AFTER_HEAD_IM;
5034    
5035                #                  ## Reprocess in the "after head" insertion mode...
5036              } else {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5037                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                  !!!cp ('t143.4');
                 !!!cp ('t144');  
5038                  #                  #
5039                } else {                } else {
5040                  !!!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;  
5041                }                }
5042    
5043                  ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5044                  !!!parse-error (type => 'unmatched end tag',
5045                                  text => 'br', token => $token);
5046                  ## Ignore the token
5047                  !!!next-token;
5048                  next B;
5049                } else {
5050                  !!!cp ('t145');
5051                  !!!parse-error (type => 'unmatched end tag',
5052                                  text => $token->{tag_name}, token => $token);
5053                  ## Ignore the token
5054                  !!!next-token;
5055                  next B;
5056              }              }
5057    
5058              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5059                !!!cp ('t146');                !!!cp ('t146');
5060                ## As if </noscript>                ## As if </noscript>
5061                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5062                !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'in noscript:/',
5063                                  text => $token->{tag_name}, token => $token);
5064                                
5065                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
5066                ## As if </head>                ## As if </head>
# Line 3864  sub _tree_construction_main ($) { Line 5076  sub _tree_construction_main ($) {
5076              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5077  ## ISSUE: This case cannot be reached?  ## ISSUE: This case cannot be reached?
5078                !!!cp ('t148');                !!!cp ('t148');
5079                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
5080                                  text => $token->{tag_name}, token => $token);
5081                ## Ignore the token ## ISSUE: An issue in the spec.                ## Ignore the token ## ISSUE: An issue in the spec.
5082                !!!next-token;                !!!next-token;
5083                redo B;                next B;
5084              } else {              } else {
5085                !!!cp ('t149');                !!!cp ('t149');
5086              }              }
# Line 3877  sub _tree_construction_main ($) { Line 5090  sub _tree_construction_main ($) {
5090              !!!insert-element ('body',, $token);              !!!insert-element ('body',, $token);
5091              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
5092              ## reprocess              ## reprocess
5093              redo B;              next B;
5094        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5095          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5096            !!!cp ('t149.1');            !!!cp ('t149.1');
5097    
5098            ## NOTE: As if <head>            ## NOTE: As if <head>
5099            !!!create-element ($self->{head_element}, 'head',, $token);            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5100            $self->{open_elements}->[-1]->[0]->append_child            $self->{open_elements}->[-1]->[0]->append_child
5101                ($self->{head_element});                ($self->{head_element});
5102            #push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            #push @{$self->{open_elements}},
5103              #    [$self->{head_element}, $el_category->{head}];
5104            #$self->{insertion_mode} = IN_HEAD_IM;            #$self->{insertion_mode} = IN_HEAD_IM;
5105            ## NOTE: Reprocess.            ## NOTE: Reprocess.
5106    
# Line 3930  sub _tree_construction_main ($) { Line 5144  sub _tree_construction_main ($) {
5144          !!!insert-element ('body',, $token);          !!!insert-element ('body',, $token);
5145          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
5146          ## NOTE: Reprocess.          ## NOTE: Reprocess.
5147          redo B;          next B;
5148        } else {        } else {
5149          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5150        }        }
# Line 3945  sub _tree_construction_main ($) { Line 5159  sub _tree_construction_main ($) {
5159              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5160    
5161              !!!next-token;              !!!next-token;
5162              redo B;              next B;
5163            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
5164              if ({              if ({
5165                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
# Line 3955  sub _tree_construction_main ($) { Line 5169  sub _tree_construction_main ($) {
5169                  ## have an element in table scope                  ## have an element in table scope
5170                  for (reverse 0..$#{$self->{open_elements}}) {                  for (reverse 0..$#{$self->{open_elements}}) {
5171                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5172                    if ($node->[1] eq 'td' or $node->[1] eq 'th') {                    if ($node->[1] & TABLE_CELL_EL) {
5173                      !!!cp ('t151');                      !!!cp ('t151');
5174    
5175                      ## Close the cell                      ## Close the cell
5176                      !!!back-token; # <?>                      !!!back-token; # <x>
5177                      $token = {type => END_TAG_TOKEN, tag_name => $node->[1],                      $token = {type => END_TAG_TOKEN,
5178                                  tag_name => $node->[0]->manakai_local_name,
5179                                line => $token->{line},                                line => $token->{line},
5180                                column => $token->{column}};                                column => $token->{column}};
5181                      redo B;                      next B;
5182                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5183                      !!!cp ('t152');                      !!!cp ('t152');
5184                      ## ISSUE: This case can never be reached, maybe.                      ## ISSUE: This case can never be reached, maybe.
5185                      last;                      last;
# Line 3975  sub _tree_construction_main ($) { Line 5188  sub _tree_construction_main ($) {
5188    
5189                  !!!cp ('t153');                  !!!cp ('t153');
5190                  !!!parse-error (type => 'start tag not allowed',                  !!!parse-error (type => 'start tag not allowed',
5191                      value => $token->{tag_name}, token => $token);                      text => $token->{tag_name}, token => $token);
5192                  ## Ignore the token                  ## Ignore the token
5193                    !!!nack ('t153.1');
5194                  !!!next-token;                  !!!next-token;
5195                  redo B;                  next B;
5196                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5197                  !!!parse-error (type => 'not closed:caption', token => $token);                  !!!parse-error (type => 'not closed', text => 'caption',
5198                                    token => $token);
5199                                    
5200                  ## NOTE: As if </caption>.                  ## NOTE: As if </caption>.
5201                  ## have a table element in table scope                  ## have a table element in table scope
# Line 3988  sub _tree_construction_main ($) { Line 5203  sub _tree_construction_main ($) {
5203                  INSCOPE: {                  INSCOPE: {
5204                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
5205                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
5206                      if ($node->[1] eq 'caption') {                      if ($node->[1] & CAPTION_EL) {
5207                        !!!cp ('t155');                        !!!cp ('t155');
5208                        $i = $_;                        $i = $_;
5209                        last INSCOPE;                        last INSCOPE;
5210                      } elsif ({                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
                               table => 1, html => 1,  
                              }->{$node->[1]}) {  
5211                        !!!cp ('t156');                        !!!cp ('t156');
5212                        last;                        last;
5213                      }                      }
# Line 4002  sub _tree_construction_main ($) { Line 5215  sub _tree_construction_main ($) {
5215    
5216                    !!!cp ('t157');                    !!!cp ('t157');
5217                    !!!parse-error (type => 'start tag not allowed',                    !!!parse-error (type => 'start tag not allowed',
5218                                    value => $token->{tag_name}, token => $token);                                    text => $token->{tag_name}, token => $token);
5219                    ## Ignore the token                    ## Ignore the token
5220                      !!!nack ('t157.1');
5221                    !!!next-token;                    !!!next-token;
5222                    redo B;                    next B;
5223                  } # INSCOPE                  } # INSCOPE
5224                                    
5225                  ## generate implied end tags                  ## generate implied end tags
5226                  while ({                  while ($self->{open_elements}->[-1]->[1]
5227                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5228                    !!!cp ('t158');                    !!!cp ('t158');
5229                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5230                  }                  }
5231    
5232                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5233                    !!!cp ('t159');                    !!!cp ('t159');
5234                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                    !!!parse-error (type => 'not closed',
5235                                      text => $self->{open_elements}->[-1]->[0]
5236                                          ->manakai_local_name,
5237                                      token => $token);
5238                  } else {                  } else {
5239                    !!!cp ('t160');                    !!!cp ('t160');
5240                  }                  }
# Line 4030  sub _tree_construction_main ($) { Line 5246  sub _tree_construction_main ($) {
5246                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5247                                    
5248                  ## reprocess                  ## reprocess
5249                  redo B;                  !!!ack-later;
5250                    next B;
5251                } else {                } else {
5252                  !!!cp ('t161');                  !!!cp ('t161');
5253                  #                  #
# Line 4046  sub _tree_construction_main ($) { Line 5263  sub _tree_construction_main ($) {
5263                  my $i;                  my $i;
5264                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5265                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5266                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5267                      !!!cp ('t163');                      !!!cp ('t163');
5268                      $i = $_;                      $i = $_;
5269                      last INSCOPE;                      last INSCOPE;
5270                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5271                      !!!cp ('t164');                      !!!cp ('t164');
5272                      last INSCOPE;                      last INSCOPE;
5273                    }                    }
5274                  } # INSCOPE                  } # INSCOPE
5275                    unless (defined $i) {                    unless (defined $i) {
5276                      !!!cp ('t165');                      !!!cp ('t165');
5277                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                      !!!parse-error (type => 'unmatched end tag',
5278                                        text => $token->{tag_name},
5279                                        token => $token);
5280                      ## Ignore the token                      ## Ignore the token
5281                      !!!next-token;                      !!!next-token;
5282                      redo B;                      next B;
5283                    }                    }
5284                                    
5285                  ## generate implied end tags                  ## generate implied end tags
5286                  while ({                  while ($self->{open_elements}->[-1]->[1]
5287                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5288                    !!!cp ('t166');                    !!!cp ('t166');
5289                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5290                  }                  }
5291    
5292                  if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5293                            ne $token->{tag_name}) {
5294                    !!!cp ('t167');                    !!!cp ('t167');
5295                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                    !!!parse-error (type => 'not closed',
5296                                      text => $self->{open_elements}->[-1]->[0]
5297                                          ->manakai_local_name,
5298                                      token => $token);
5299                  } else {                  } else {
5300                    !!!cp ('t168');                    !!!cp ('t168');
5301                  }                  }
# Line 4087  sub _tree_construction_main ($) { Line 5307  sub _tree_construction_main ($) {
5307                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5308                                    
5309                  !!!next-token;                  !!!next-token;
5310                  redo B;                  next B;
5311                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5312                  !!!cp ('t169');                  !!!cp ('t169');
5313                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5314                                    text => $token->{tag_name}, token => $token);
5315                  ## Ignore the token                  ## Ignore the token
5316                  !!!next-token;                  !!!next-token;
5317                  redo B;                  next B;
5318                } else {                } else {
5319                  !!!cp ('t170');                  !!!cp ('t170');
5320                  #                  #
# Line 4105  sub _tree_construction_main ($) { Line 5326  sub _tree_construction_main ($) {
5326                  INSCOPE: {                  INSCOPE: {
5327                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
5328                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
5329                      if ($node->[1] eq $token->{tag_name}) {                      if ($node->[1] & CAPTION_EL) {
5330                        !!!cp ('t171');                        !!!cp ('t171');
5331                        $i = $_;                        $i = $_;
5332                        last INSCOPE;                        last INSCOPE;
5333                      } elsif ({                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
                               table => 1, html => 1,  
                              }->{$node->[1]}) {  
5334                        !!!cp ('t172');                        !!!cp ('t172');
5335                        last;                        last;
5336                      }                      }
# Line 4119  sub _tree_construction_main ($) { Line 5338  sub _tree_construction_main ($) {
5338    
5339                    !!!cp ('t173');                    !!!cp ('t173');
5340                    !!!parse-error (type => 'unmatched end tag',                    !!!parse-error (type => 'unmatched end tag',
5341                                    value => $token->{tag_name}, token => $token);                                    text => $token->{tag_name}, token => $token);
5342                    ## Ignore the token                    ## Ignore the token
5343                    !!!next-token;                    !!!next-token;
5344                    redo B;                    next B;
5345                  } # INSCOPE                  } # INSCOPE
5346                                    
5347                  ## generate implied end tags                  ## generate implied end tags
5348                  while ({                  while ($self->{open_elements}->[-1]->[1]
5349                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5350                    !!!cp ('t174');                    !!!cp ('t174');
5351                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5352                  }                  }
5353                                    
5354                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5355                    !!!cp ('t175');                    !!!cp ('t175');
5356                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                    !!!parse-error (type => 'not closed',
5357                                      text => $self->{open_elements}->[-1]->[0]
5358                                          ->manakai_local_name,
5359                                      token => $token);
5360                  } else {                  } else {
5361                    !!!cp ('t176');                    !!!cp ('t176');
5362                  }                  }
# Line 4147  sub _tree_construction_main ($) { Line 5368  sub _tree_construction_main ($) {
5368                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5369                                    
5370                  !!!next-token;                  !!!next-token;
5371                  redo B;                  next B;
5372                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5373                  !!!cp ('t177');                  !!!cp ('t177');
5374                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5375                                    text => $token->{tag_name}, token => $token);
5376                  ## Ignore the token                  ## Ignore the token
5377                  !!!next-token;                  !!!next-token;
5378                  redo B;                  next B;
5379                } else {                } else {
5380                  !!!cp ('t178');                  !!!cp ('t178');
5381                  #                  #
# Line 4169  sub _tree_construction_main ($) { Line 5391  sub _tree_construction_main ($) {
5391                INSCOPE: {                INSCOPE: {
5392                  for (reverse 0..$#{$self->{open_elements}}) {                  for (reverse 0..$#{$self->{open_elements}}) {
5393                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5394                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5395                      !!!cp ('t179');                      !!!cp ('t179');
5396                      $i = $_;                      $i = $_;
5397    
5398                      ## Close the cell                      ## Close the cell
5399                      !!!back-token; # </?>                      !!!back-token; # </x>
5400                      $token = {type => END_TAG_TOKEN, tag_name => $tn,                      $token = {type => END_TAG_TOKEN, tag_name => $tn,
5401                                line => $token->{line},                                line => $token->{line},
5402                                column => $token->{column}};                                column => $token->{column}};
5403                      redo B;                      next B;
5404                    } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {                    } elsif ($node->[1] & TABLE_CELL_EL) {
5405                      !!!cp ('t180');                      !!!cp ('t180');
5406                      $tn = $node->[1];                      $tn = $node->[0]->manakai_local_name;
5407                      ## NOTE: There is exactly one |td| or |th| element                      ## NOTE: There is exactly one |td| or |th| element
5408                      ## in scope in the stack of open elements by definition.                      ## in scope in the stack of open elements by definition.
5409                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5410                      ## ISSUE: Can this be reached?                      ## ISSUE: Can this be reached?
5411                      !!!cp ('t181');                      !!!cp ('t181');
5412                      last;                      last;
# Line 4195  sub _tree_construction_main ($) { Line 5415  sub _tree_construction_main ($) {
5415    
5416                  !!!cp ('t182');                  !!!cp ('t182');
5417                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
5418                      value => $token->{tag_name}, token => $token);                      text => $token->{tag_name}, token => $token);
5419                  ## Ignore the token                  ## Ignore the token
5420                  !!!next-token;                  !!!next-token;
5421                  redo B;                  next B;
5422                } # INSCOPE                } # INSCOPE
5423              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
5424                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5425                !!!parse-error (type => 'not closed:caption', token => $token);                !!!parse-error (type => 'not closed', text => 'caption',
5426                                  token => $token);
5427    
5428                ## As if </caption>                ## As if </caption>
5429                ## have a table element in table scope                ## have a table element in table scope
5430                my $i;                my $i;
5431                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5432                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5433                  if ($node->[1] eq 'caption') {                  if ($node->[1] & CAPTION_EL) {
5434                    !!!cp ('t184');                    !!!cp ('t184');
5435                    $i = $_;                    $i = $_;
5436                    last INSCOPE;                    last INSCOPE;
5437                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
5438                    !!!cp ('t185');                    !!!cp ('t185');
5439                    last INSCOPE;                    last INSCOPE;
5440                  }                  }
5441                } # INSCOPE                } # INSCOPE
5442                unless (defined $i) {                unless (defined $i) {
5443                  !!!cp ('t186');                  !!!cp ('t186');
5444                  !!!parse-error (type => 'unmatched end tag:caption', token => $token);                  !!!parse-error (type => 'unmatched end tag',
5445                                    text => 'caption', token => $token);
5446                  ## Ignore the token                  ## Ignore the token
5447                  !!!next-token;                  !!!next-token;
5448                  redo B;                  next B;
5449                }                }
5450                                
5451                ## generate implied end tags                ## generate implied end tags
5452                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
5453                  !!!cp ('t187');                  !!!cp ('t187');
5454                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5455                }                }
5456    
5457                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5458                  !!!cp ('t188');                  !!!cp ('t188');
5459                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                  !!!parse-error (type => 'not closed',
5460                                    text => $self->{open_elements}->[-1]->[0]
5461                                        ->manakai_local_name,
5462                                    token => $token);
5463                } else {                } else {
5464                  !!!cp ('t189');                  !!!cp ('t189');
5465                }                }
# Line 4250  sub _tree_construction_main ($) { Line 5471  sub _tree_construction_main ($) {
5471                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
5472    
5473                ## reprocess                ## reprocess
5474                redo B;                next B;
5475              } elsif ({              } elsif ({
5476                        body => 1, col => 1, colgroup => 1, html => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
5477                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5478                if ($self->{insertion_mode} & BODY_TABLE_IMS) {                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5479                  !!!cp ('t190');                  !!!cp ('t190');
5480                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5481                                    text => $token->{tag_name}, token => $token);
5482                  ## Ignore the token                  ## Ignore the token
5483                  !!!next-token;                  !!!next-token;
5484                  redo B;                  next B;
5485                } else {                } else {
5486                  !!!cp ('t191');                  !!!cp ('t191');
5487                  #                  #
# Line 4270  sub _tree_construction_main ($) { Line 5492  sub _tree_construction_main ($) {
5492                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5493                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5494                !!!cp ('t192');                !!!cp ('t192');
5495                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
5496                                  text => $token->{tag_name}, token => $token);
5497                ## Ignore the token                ## Ignore the token
5498                !!!next-token;                !!!next-token;
5499                redo B;                next B;
5500              } else {              } else {
5501                !!!cp ('t193');                !!!cp ('t193');
5502                #                #
5503              }              }
5504        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5505          for my $entry (@{$self->{open_elements}}) {          for my $entry (@{$self->{open_elements}}) {
5506            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]}) {  
5507              !!!cp ('t75');              !!!cp ('t75');
5508              !!!parse-error (type => 'in body:#eof', token => $token);              !!!parse-error (type => 'in body:#eof', token => $token);
5509              last;              last;
# Line 4301  sub _tree_construction_main ($) { Line 5521  sub _tree_construction_main ($) {
5521      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
5522        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
5523          if (not $open_tables->[-1]->[1] and # tainted          if (not $open_tables->[-1]->[1] and # tainted
5524              $token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5525            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5526                                
5527            unless (length $token->{data}) {            unless (length $token->{data}) {
5528              !!!cp ('t194');              !!!cp ('t194');
5529              !!!next-token;              !!!next-token;
5530              redo B;              next B;
5531            } else {            } else {
5532              !!!cp ('t195');              !!!cp ('t195');
5533            }            }
5534          }          }
5535    
5536              !!!parse-error (type => 'in table:#character', token => $token);          !!!parse-error (type => 'in table:#text', token => $token);
5537    
5538              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
5539              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
# Line 4321  sub _tree_construction_main ($) { Line 5541  sub _tree_construction_main ($) {
5541              ## result in a new Text node.              ## result in a new Text node.
5542              $reconstruct_active_formatting_elements->($insert_to_foster);              $reconstruct_active_formatting_elements->($insert_to_foster);
5543                            
5544              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]}) {  
5545                # MUST                # MUST
5546                my $foster_parent_element;                my $foster_parent_element;
5547                my $next_sibling;                my $next_sibling;
5548                my $prev_sibling;                my $prev_sibling;
5549                OE: for (reverse 0..$#{$self->{open_elements}}) {                OE: for (reverse 0..$#{$self->{open_elements}}) {
5550                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5551                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5552                    if (defined $parent and $parent->node_type == 1) {                    if (defined $parent and $parent->node_type == 1) {
5553                      !!!cp ('t196');                      !!!cp ('t196');
# Line 4365  sub _tree_construction_main ($) { Line 5582  sub _tree_construction_main ($) {
5582          }          }
5583                            
5584          !!!next-token;          !!!next-token;
5585          redo B;          next B;
5586        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5587              if ({          if ({
5588                   tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => ($self->{insertion_mode} != IN_ROW_IM),
5589                   th => 1, td => 1,               th => 1, td => 1,
5590                  }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
5591                if ($self->{insertion_mode} == IN_TABLE_IM) {            if ($self->{insertion_mode} == IN_TABLE_IM) {
5592                  ## Clear back to table context              ## Clear back to table context
5593                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5594                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5595                    !!!cp ('t201');                !!!cp ('t201');
5596                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5597                  }              }
5598                                
5599                  !!!insert-element ('tbody',, $token);              !!!insert-element ('tbody',, $token);
5600                  $self->{insertion_mode} = IN_TABLE_BODY_IM;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5601                  ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
5602                }            }
5603              
5604                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5605                  unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
5606                    !!!cp ('t202');                !!!cp ('t202');
5607                    !!!parse-error (type => 'missing start tag:tr', token => $token);                !!!parse-error (type => 'missing start tag:tr', token => $token);
5608                  }              }
5609                                    
5610                  ## Clear back to table body context              ## Clear back to table body context
5611                  while (not {              while (not ($self->{open_elements}->[-1]->[1]
5612                    tbody => 1, tfoot => 1, thead => 1, html => 1,                              & TABLE_ROWS_SCOPING_EL)) {
5613                  }->{$self->{open_elements}->[-1]->[1]}) {                !!!cp ('t203');
5614                    !!!cp ('t203');                ## ISSUE: Can this case be reached?
5615                    ## ISSUE: Can this case be reached?                pop @{$self->{open_elements}};
5616                    pop @{$self->{open_elements}};              }
                 }  
5617                                    
5618                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5619                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
5620                    !!!cp ('t204');                    !!!cp ('t204');
5621                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5622                      !!!nack ('t204');
5623                    !!!next-token;                    !!!next-token;
5624                    redo B;                    next B;
5625                  } else {                  } else {
5626                    !!!cp ('t205');                    !!!cp ('t205');
5627                    !!!insert-element ('tr',, $token);                    !!!insert-element ('tr',, $token);
# Line 4415  sub _tree_construction_main ($) { Line 5632  sub _tree_construction_main ($) {
5632                }                }
5633    
5634                ## Clear back to table row context                ## Clear back to table row context
5635                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5636                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
5637                  !!!cp ('t207');                  !!!cp ('t207');
5638                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5639                }                }
# Line 4427  sub _tree_construction_main ($) { Line 5643  sub _tree_construction_main ($) {
5643    
5644                push @$active_formatting_elements, ['#marker', ''];                push @$active_formatting_elements, ['#marker', ''];
5645                                
5646                  !!!nack ('t207.1');
5647                !!!next-token;                !!!next-token;
5648                redo B;                next B;
5649              } elsif ({              } elsif ({
5650                        caption => 1, col => 1, colgroup => 1,                        caption => 1, col => 1, colgroup => 1,
5651                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
# Line 4440  sub _tree_construction_main ($) { Line 5657  sub _tree_construction_main ($) {
5657                  my $i;                  my $i;
5658                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5659                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5660                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
5661                      !!!cp ('t208');                      !!!cp ('t208');
5662                      $i = $_;                      $i = $_;
5663                      last INSCOPE;                      last INSCOPE;
5664                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             html => 1,  
   
                             ## NOTE: This element does not appear here, maybe.  
                             table => 1,  
                            }->{$node->[1]}) {  
5665                      !!!cp ('t209');                      !!!cp ('t209');
5666                      last INSCOPE;                      last INSCOPE;
5667                    }                    }
5668                  } # INSCOPE                  } # INSCOPE
5669                  unless (defined $i) {                  unless (defined $i) {
5670                   !!!cp ('t210');                    !!!cp ('t210');
5671  ## TODO: This type is wrong.  ## TODO: This type is wrong.
5672                   !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name}, token => $token);                    !!!parse-error (type => 'unmacthed end tag',
5673                                      text => $token->{tag_name}, token => $token);
5674                    ## Ignore the token                    ## Ignore the token
5675                      !!!nack ('t210.1');
5676                    !!!next-token;                    !!!next-token;
5677                    redo B;                    next B;
5678                  }                  }
5679                                    
5680                  ## Clear back to table row context                  ## Clear back to table row context
5681                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5682                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5683                    !!!cp ('t211');                    !!!cp ('t211');
5684                    ## ISSUE: Can this case be reached?                    ## ISSUE: Can this case be reached?
5685                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4477  sub _tree_construction_main ($) { Line 5690  sub _tree_construction_main ($) {
5690                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
5691                    !!!cp ('t212');                    !!!cp ('t212');
5692                    ## reprocess                    ## reprocess
5693                    redo B;                    !!!ack-later;
5694                      next B;
5695                  } else {                  } else {
5696                    !!!cp ('t213');                    !!!cp ('t213');
5697                    ## reprocess in the "in table body" insertion mode...                    ## reprocess in the "in table body" insertion mode...
# Line 4489  sub _tree_construction_main ($) { Line 5703  sub _tree_construction_main ($) {
5703                  my $i;                  my $i;
5704                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5705                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5706                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
5707                      !!!cp ('t214');                      !!!cp ('t214');
5708                      $i = $_;                      $i = $_;
5709                      last INSCOPE;                      last INSCOPE;
5710                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5711                      !!!cp ('t215');                      !!!cp ('t215');
5712                      last INSCOPE;                      last INSCOPE;
5713                    }                    }
5714                  } # INSCOPE                  } # INSCOPE
5715                  unless (defined $i) {                  unless (defined $i) {
5716                    !!!cp ('t216');                    !!!cp ('t216');
5717  ## TODO: This erorr type ios wrong.  ## TODO: This erorr type is wrong.
5718                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                    !!!parse-error (type => 'unmatched end tag',
5719                                      text => $token->{tag_name}, token => $token);
5720                    ## Ignore the token                    ## Ignore the token
5721                      !!!nack ('t216.1');
5722                    !!!next-token;                    !!!next-token;
5723                    redo B;                    next B;
5724                  }                  }
5725    
5726                  ## Clear back to table body context                  ## Clear back to table body context
5727                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5728                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5729                    !!!cp ('t217');                    !!!cp ('t217');
5730                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
5731                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4536  sub _tree_construction_main ($) { Line 5747  sub _tree_construction_main ($) {
5747    
5748                if ($token->{tag_name} eq 'col') {                if ($token->{tag_name} eq 'col') {
5749                  ## Clear back to table context                  ## Clear back to table context
5750                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                  while (not ($self->{open_elements}->[-1]->[1]
5751                         $self->{open_elements}->[-1]->[1] ne 'html') {                                  & TABLE_SCOPING_EL)) {
5752                    !!!cp ('t219');                    !!!cp ('t219');
5753                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
5754                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4546  sub _tree_construction_main ($) { Line 5757  sub _tree_construction_main ($) {
5757                  !!!insert-element ('colgroup',, $token);                  !!!insert-element ('colgroup',, $token);
5758                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5759                  ## reprocess                  ## reprocess
5760                  redo B;                  !!!ack-later;
5761                    next B;
5762                } elsif ({                } elsif ({
5763                          caption => 1,                          caption => 1,
5764                          colgroup => 1,                          colgroup => 1,
5765                          tbody => 1, tfoot => 1, thead => 1,                          tbody => 1, tfoot => 1, thead => 1,
5766                         }->{$token->{tag_name}}) {                         }->{$token->{tag_name}}) {
5767                  ## Clear back to table context                  ## Clear back to table context
5768                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                  while (not ($self->{open_elements}->[-1]->[1]
5769                         $self->{open_elements}->[-1]->[1] ne 'html') {                                  & TABLE_SCOPING_EL)) {
5770                    !!!cp ('t220');                    !!!cp ('t220');
5771                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
5772                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4572  sub _tree_construction_main ($) { Line 5784  sub _tree_construction_main ($) {
5784                                             thead => IN_TABLE_BODY_IM,                                             thead => IN_TABLE_BODY_IM,
5785                                            }->{$token->{tag_name}};                                            }->{$token->{tag_name}};
5786                  !!!next-token;                  !!!next-token;
5787                  redo B;                  !!!nack ('t220.1');
5788                    next B;
5789                } else {                } else {
5790                  die "$0: in table: <>: $token->{tag_name}";                  die "$0: in table: <>: $token->{tag_name}";
5791                }                }
5792              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5793                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                !!!parse-error (type => 'not closed',
5794                                  text => $self->{open_elements}->[-1]->[0]
5795                                      ->manakai_local_name,
5796                                  token => $token);
5797    
5798                ## As if </table>                ## As if </table>
5799                ## have a table element in table scope                ## have a table element in table scope
5800                my $i;                my $i;
5801                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5802                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5803                  if ($node->[1] eq 'table') {                  if ($node->[1] & TABLE_EL) {
5804                    !!!cp ('t221');                    !!!cp ('t221');
5805                    $i = $_;                    $i = $_;
5806                    last INSCOPE;                    last INSCOPE;
5807                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           #table => 1,  
                           html => 1,  
                          }->{$node->[1]}) {  
5808                    !!!cp ('t222');                    !!!cp ('t222');
5809                    last INSCOPE;                    last INSCOPE;
5810                  }                  }
# Line 4599  sub _tree_construction_main ($) { Line 5812  sub _tree_construction_main ($) {
5812                unless (defined $i) {                unless (defined $i) {
5813                  !!!cp ('t223');                  !!!cp ('t223');
5814  ## TODO: The following is wrong, maybe.  ## TODO: The following is wrong, maybe.
5815                  !!!parse-error (type => 'unmatched end tag:table', token => $token);                  !!!parse-error (type => 'unmatched end tag', text => 'table',
5816                                    token => $token);
5817                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5818                    !!!nack ('t223.1');
5819                  !!!next-token;                  !!!next-token;
5820                  redo B;                  next B;
5821                }                }
5822                                
5823  ## TODO: Followings are removed from the latest spec.  ## TODO: Followings are removed from the latest spec.
5824                ## generate implied end tags                ## generate implied end tags
5825                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
5826                  !!!cp ('t224');                  !!!cp ('t224');
5827                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5828                }                }
5829    
5830                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
5831                  !!!cp ('t225');                  !!!cp ('t225');
5832  ## ISSUE: Can this case be reached?                  ## NOTE: |<table><tr><table>|
5833                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                  !!!parse-error (type => 'not closed',
5834                                    text => $self->{open_elements}->[-1]->[0]
5835                                        ->manakai_local_name,
5836                                    token => $token);
5837                } else {                } else {
5838                  !!!cp ('t226');                  !!!cp ('t226');
5839                }                }
# Line 4627  sub _tree_construction_main ($) { Line 5843  sub _tree_construction_main ($) {
5843    
5844                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5845    
5846                ## reprocess            ## reprocess
5847                redo B;            !!!ack-later;
5848              next B;
5849          } elsif ($token->{tag_name} eq 'style') {          } elsif ($token->{tag_name} eq 'style') {
5850            if (not $open_tables->[-1]->[1]) { # tainted            if (not $open_tables->[-1]->[1]) { # tainted
5851              !!!cp ('t227.8');              !!!cp ('t227.8');
5852              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
5853              $parse_rcdata->(CDATA_CONTENT_MODEL);              $parse_rcdata->(CDATA_CONTENT_MODEL);
5854              redo B;              next B;
5855            } else {            } else {
5856              !!!cp ('t227.7');              !!!cp ('t227.7');
5857              #              #
# Line 4644  sub _tree_construction_main ($) { Line 5861  sub _tree_construction_main ($) {
5861              !!!cp ('t227.6');              !!!cp ('t227.6');
5862              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
5863              $script_start_tag->();              $script_start_tag->();
5864              redo B;              next B;
5865            } else {            } else {
5866              !!!cp ('t227.5');              !!!cp ('t227.5');
5867              #              #
# Line 4655  sub _tree_construction_main ($) { Line 5872  sub _tree_construction_main ($) {
5872                my $type = lc $token->{attributes}->{type}->{value};                my $type = lc $token->{attributes}->{type}->{value};
5873                if ($type eq 'hidden') {                if ($type eq 'hidden') {
5874                  !!!cp ('t227.3');                  !!!cp ('t227.3');
5875                  !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'in table',
5876                                    text => $token->{tag_name}, token => $token);
5877    
5878                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5879    
# Line 4664  sub _tree_construction_main ($) { Line 5882  sub _tree_construction_main ($) {
5882                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5883    
5884                  !!!next-token;                  !!!next-token;
5885                  redo B;                  !!!ack ('t227.2.1');
5886                    next B;
5887                } else {                } else {
5888                  !!!cp ('t227.2');                  !!!cp ('t227.2');
5889                  #                  #
# Line 4682  sub _tree_construction_main ($) { Line 5901  sub _tree_construction_main ($) {
5901            #            #
5902          }          }
5903    
5904          !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);          !!!parse-error (type => 'in table', text => $token->{tag_name},
5905                            token => $token);
5906    
5907          $insert = $insert_to_foster;          $insert = $insert_to_foster;
5908          #          #
# Line 4693  sub _tree_construction_main ($) { Line 5913  sub _tree_construction_main ($) {
5913                my $i;                my $i;
5914                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5915                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5916                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_ROW_EL) {
5917                    !!!cp ('t228');                    !!!cp ('t228');
5918                    $i = $_;                    $i = $_;
5919                    last INSCOPE;                    last INSCOPE;
5920                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
5921                    !!!cp ('t229');                    !!!cp ('t229');
5922                    last INSCOPE;                    last INSCOPE;
5923                  }                  }
5924                } # INSCOPE                } # INSCOPE
5925                unless (defined $i) {                unless (defined $i) {
5926                  !!!cp ('t230');                  !!!cp ('t230');
5927                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5928                                    text => $token->{tag_name}, token => $token);
5929                  ## Ignore the token                  ## Ignore the token
5930                    !!!nack ('t230.1');
5931                  !!!next-token;                  !!!next-token;
5932                  redo B;                  next B;
5933                } else {                } else {
5934                  !!!cp ('t232');                  !!!cp ('t232');
5935                }                }
5936    
5937                ## Clear back to table row context                ## Clear back to table row context
5938                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5939                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
5940                  !!!cp ('t231');                  !!!cp ('t231');
5941  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
5942                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
# Line 4726  sub _tree_construction_main ($) { Line 5945  sub _tree_construction_main ($) {
5945                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
5946                $self->{insertion_mode} = IN_TABLE_BODY_IM;                $self->{insertion_mode} = IN_TABLE_BODY_IM;
5947                !!!next-token;                !!!next-token;
5948                redo B;                !!!nack ('t231.1');
5949                  next B;
5950              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5951                if ($self->{insertion_mode} == IN_ROW_IM) {                if ($self->{insertion_mode} == IN_ROW_IM) {
5952                  ## As if </tr>                  ## As if </tr>
# Line 4734  sub _tree_construction_main ($) { Line 5954  sub _tree_construction_main ($) {
5954                  my $i;                  my $i;
5955                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5956                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5957                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
5958                      !!!cp ('t233');                      !!!cp ('t233');
5959                      $i = $_;                      $i = $_;
5960                      last INSCOPE;                      last INSCOPE;
5961                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5962                      !!!cp ('t234');                      !!!cp ('t234');
5963                      last INSCOPE;                      last INSCOPE;
5964                    }                    }
# Line 4748  sub _tree_construction_main ($) { Line 5966  sub _tree_construction_main ($) {
5966                  unless (defined $i) {                  unless (defined $i) {
5967                    !!!cp ('t235');                    !!!cp ('t235');
5968  ## TODO: The following is wrong.  ## TODO: The following is wrong.
5969                    !!!parse-error (type => 'unmatched end tag:'.$token->{type}, token => $token);                    !!!parse-error (type => 'unmatched end tag',
5970                                      text => $token->{type}, token => $token);
5971                    ## Ignore the token                    ## Ignore the token
5972                      !!!nack ('t236.1');
5973                    !!!next-token;                    !!!next-token;
5974                    redo B;                    next B;
5975                  }                  }
5976                                    
5977                  ## Clear back to table row context                  ## Clear back to table row context
5978                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5979                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5980                    !!!cp ('t236');                    !!!cp ('t236');
5981  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
5982                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4773  sub _tree_construction_main ($) { Line 5992  sub _tree_construction_main ($) {
5992                  my $i;                  my $i;
5993                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5994                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5995                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
5996                      !!!cp ('t237');                      !!!cp ('t237');
5997                      $i = $_;                      $i = $_;
5998                      last INSCOPE;                      last INSCOPE;
5999                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6000                      !!!cp ('t238');                      !!!cp ('t238');
6001                      last INSCOPE;                      last INSCOPE;
6002                    }                    }
6003                  } # INSCOPE                  } # INSCOPE
6004                  unless (defined $i) {                  unless (defined $i) {
6005                    !!!cp ('t239');                    !!!cp ('t239');
6006                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                    !!!parse-error (type => 'unmatched end tag',
6007                                      text => $token->{tag_name}, token => $token);
6008                    ## Ignore the token                    ## Ignore the token
6009                      !!!nack ('t239.1');
6010                    !!!next-token;                    !!!next-token;
6011                    redo B;                    next B;
6012                  }                  }
6013                                    
6014                  ## Clear back to table body context                  ## Clear back to table body context
6015                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6016                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6017                    !!!cp ('t240');                    !!!cp ('t240');
6018                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6019                  }                  }
# Line 4823  sub _tree_construction_main ($) { Line 6039  sub _tree_construction_main ($) {
6039                my $i;                my $i;
6040                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6041                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6042                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_EL) {
6043                    !!!cp ('t241');                    !!!cp ('t241');
6044                    $i = $_;                    $i = $_;
6045                    last INSCOPE;                    last INSCOPE;
6046                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6047                    !!!cp ('t242');                    !!!cp ('t242');
6048                    last INSCOPE;                    last INSCOPE;
6049                  }                  }
6050                } # INSCOPE                } # INSCOPE
6051                unless (defined $i) {                unless (defined $i) {
6052                  !!!cp ('t243');                  !!!cp ('t243');
6053                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
6054                                    text => $token->{tag_name}, token => $token);
6055                  ## Ignore the token                  ## Ignore the token
6056                    !!!nack ('t243.1');
6057                  !!!next-token;                  !!!next-token;
6058                  redo B;                  next B;
6059                }                }
6060                                    
6061                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
# Line 4848  sub _tree_construction_main ($) { Line 6064  sub _tree_construction_main ($) {
6064                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
6065                                
6066                !!!next-token;                !!!next-token;
6067                redo B;                next B;
6068              } elsif ({              } elsif ({
6069                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
6070                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
# Line 4858  sub _tree_construction_main ($) { Line 6074  sub _tree_construction_main ($) {
6074                  my $i;                  my $i;
6075                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6076                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6077                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6078                      !!!cp ('t247');                      !!!cp ('t247');
6079                      $i = $_;                      $i = $_;
6080                      last INSCOPE;                      last INSCOPE;
6081                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6082                      !!!cp ('t248');                      !!!cp ('t248');
6083                      last INSCOPE;                      last INSCOPE;
6084                    }                    }
6085                  } # INSCOPE                  } # INSCOPE
6086                    unless (defined $i) {                    unless (defined $i) {
6087                      !!!cp ('t249');                      !!!cp ('t249');
6088                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                      !!!parse-error (type => 'unmatched end tag',
6089                                        text => $token->{tag_name}, token => $token);
6090                      ## Ignore the token                      ## Ignore the token
6091                        !!!nack ('t249.1');
6092                      !!!next-token;                      !!!next-token;
6093                      redo B;                      next B;
6094                    }                    }
6095                                    
6096                  ## As if </tr>                  ## As if </tr>
# Line 4882  sub _tree_construction_main ($) { Line 6098  sub _tree_construction_main ($) {
6098                  my $i;                  my $i;
6099                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6100                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6101                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
6102                      !!!cp ('t250');                      !!!cp ('t250');
6103                      $i = $_;                      $i = $_;
6104                      last INSCOPE;                      last INSCOPE;
6105                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6106                      !!!cp ('t251');                      !!!cp ('t251');
6107                      last INSCOPE;                      last INSCOPE;
6108                    }                    }
6109                  } # INSCOPE                  } # INSCOPE
6110                    unless (defined $i) {                    unless (defined $i) {
6111                      !!!cp ('t252');                      !!!cp ('t252');
6112                      !!!parse-error (type => 'unmatched end tag:tr', token => $token);                      !!!parse-error (type => 'unmatched end tag',
6113                                        text => 'tr', token => $token);
6114                      ## Ignore the token                      ## Ignore the token
6115                        !!!nack ('t252.1');
6116                      !!!next-token;                      !!!next-token;
6117                      redo B;                      next B;
6118                    }                    }
6119                                    
6120                  ## Clear back to table row context                  ## Clear back to table row context
6121                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6122                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6123                    !!!cp ('t253');                    !!!cp ('t253');
6124  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
6125                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4919  sub _tree_construction_main ($) { Line 6134  sub _tree_construction_main ($) {
6134                my $i;                my $i;
6135                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6136                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6137                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6138                    !!!cp ('t254');                    !!!cp ('t254');
6139                    $i = $_;                    $i = $_;
6140                    last INSCOPE;                    last INSCOPE;
6141                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6142                    !!!cp ('t255');                    !!!cp ('t255');
6143                    last INSCOPE;                    last INSCOPE;
6144                  }                  }
6145                } # INSCOPE                } # INSCOPE
6146                unless (defined $i) {                unless (defined $i) {
6147                  !!!cp ('t256');                  !!!cp ('t256');
6148                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
6149                                    text => $token->{tag_name}, token => $token);
6150                  ## Ignore the token                  ## Ignore the token
6151                    !!!nack ('t256.1');
6152                  !!!next-token;                  !!!next-token;
6153                  redo B;                  next B;
6154                }                }
6155    
6156                ## Clear back to table body context                ## Clear back to table body context
6157                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6158                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
6159                  !!!cp ('t257');                  !!!cp ('t257');
6160  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
6161                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
# Line 4949  sub _tree_construction_main ($) { Line 6163  sub _tree_construction_main ($) {
6163    
6164                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6165                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
6166                  !!!nack ('t257.1');
6167                !!!next-token;                !!!next-token;
6168                redo B;                next B;
6169              } elsif ({              } elsif ({
6170                        body => 1, caption => 1, col => 1, colgroup => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6171                        html => 1, td => 1, th => 1,                        html => 1, td => 1, th => 1,
6172                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6173                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6174                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6175                !!!cp ('t258');            !!!cp ('t258');
6176                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
6177                ## Ignore the token                            text => $token->{tag_name}, token => $token);
6178                !!!next-token;            ## Ignore the token
6179                redo B;            !!!nack ('t258.1');
6180               !!!next-token;
6181              next B;
6182          } else {          } else {
6183            !!!cp ('t259');            !!!cp ('t259');
6184            !!!parse-error (type => 'in table:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'in table:/',
6185                              text => $token->{tag_name}, token => $token);
6186    
6187            $insert = $insert_to_foster;            $insert = $insert_to_foster;
6188            #            #
6189          }          }
6190        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6191          unless ($self->{open_elements}->[-1]->[1] eq 'html' and          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6192                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
6193            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
6194            !!!cp ('t259.1');            !!!cp ('t259.1');
# Line 4987  sub _tree_construction_main ($) { Line 6205  sub _tree_construction_main ($) {
6205        }        }
6206      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6207            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6208              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6209                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6210                unless (length $token->{data}) {                unless (length $token->{data}) {
6211                  !!!cp ('t260');                  !!!cp ('t260');
6212                  !!!next-token;                  !!!next-token;
6213                  redo B;                  next B;
6214                }                }
6215              }              }
6216                            
# Line 5003  sub _tree_construction_main ($) { Line 6221  sub _tree_construction_main ($) {
6221                !!!cp ('t262');                !!!cp ('t262');
6222                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6223                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6224                  !!!ack ('t262.1');
6225                !!!next-token;                !!!next-token;
6226                redo B;                next B;
6227              } else {              } else {
6228                !!!cp ('t263');                !!!cp ('t263');
6229                #                #
6230              }              }
6231            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
6232              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
6233                if ($self->{open_elements}->[-1]->[1] eq 'html') {                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6234                  !!!cp ('t264');                  !!!cp ('t264');
6235                  !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);                  !!!parse-error (type => 'unmatched end tag',
6236                                    text => 'colgroup', token => $token);
6237                  ## Ignore the token                  ## Ignore the token
6238                  !!!next-token;                  !!!next-token;
6239                  redo B;                  next B;
6240                } else {                } else {
6241                  !!!cp ('t265');                  !!!cp ('t265');
6242                  pop @{$self->{open_elements}}; # colgroup                  pop @{$self->{open_elements}}; # colgroup
6243                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
6244                  !!!next-token;                  !!!next-token;
6245                  redo B;                              next B;            
6246                }                }
6247              } elsif ($token->{tag_name} eq 'col') {              } elsif ($token->{tag_name} eq 'col') {
6248                !!!cp ('t266');                !!!cp ('t266');
6249                !!!parse-error (type => 'unmatched end tag:col', token => $token);                !!!parse-error (type => 'unmatched end tag',
6250                                  text => 'col', token => $token);
6251                ## Ignore the token                ## Ignore the token
6252                !!!next-token;                !!!next-token;
6253                redo B;                next B;
6254              } else {              } else {
6255                !!!cp ('t267');                !!!cp ('t267');
6256                #                #
6257              }              }
6258        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6259          if ($self->{open_elements}->[-1]->[1] eq 'html' or          if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6260              @{$self->{open_elements}} == 1) { # redundant, maybe              @{$self->{open_elements}} == 1) { # redundant, maybe
6261            !!!cp ('t270.2');            !!!cp ('t270.2');
6262            ## Stop parsing.            ## Stop parsing.
# Line 5046  sub _tree_construction_main ($) { Line 6267  sub _tree_construction_main ($) {
6267            pop @{$self->{open_elements}}; # colgroup            pop @{$self->{open_elements}}; # colgroup
6268            $self->{insertion_mode} = IN_TABLE_IM;            $self->{insertion_mode} = IN_TABLE_IM;
6269            ## Reprocess.            ## Reprocess.
6270            redo B;            next B;
6271          }          }
6272        } else {        } else {
6273          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6274        }        }
6275    
6276            ## As if </colgroup>            ## As if </colgroup>
6277            if ($self->{open_elements}->[-1]->[1] eq 'html') {            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6278              !!!cp ('t269');              !!!cp ('t269');
6279  ## TODO: Wrong error type?  ## TODO: Wrong error type?
6280              !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);              !!!parse-error (type => 'unmatched end tag',
6281                                text => 'colgroup', token => $token);
6282              ## Ignore the token              ## Ignore the token
6283                !!!nack ('t269.1');
6284              !!!next-token;              !!!next-token;
6285              redo B;              next B;
6286            } else {            } else {
6287              !!!cp ('t270');              !!!cp ('t270');
6288              pop @{$self->{open_elements}}; # colgroup              pop @{$self->{open_elements}}; # colgroup
6289              $self->{insertion_mode} = IN_TABLE_IM;              $self->{insertion_mode} = IN_TABLE_IM;
6290                !!!ack-later;
6291              ## reprocess              ## reprocess
6292              redo B;              next B;
6293            }            }
6294      } elsif ($self->{insertion_mode} & SELECT_IMS) {      } elsif ($self->{insertion_mode} & SELECT_IMS) {
6295        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6296          !!!cp ('t271');          !!!cp ('t271');
6297          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6298          !!!next-token;          !!!next-token;
6299          redo B;          next B;
6300        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6301              if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
6302                if ($self->{open_elements}->[-1]->[1] eq 'option') {            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6303                  !!!cp ('t272');              !!!cp ('t272');
6304                  ## As if </option>              ## As if </option>
6305                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6306                } else {            } else {
6307                  !!!cp ('t273');              !!!cp ('t273');
6308                }            }
6309    
6310                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6311                !!!next-token;            !!!nack ('t273.1');
6312                redo B;            !!!next-token;
6313              } elsif ($token->{tag_name} eq 'optgroup') {            next B;
6314                if ($self->{open_elements}->[-1]->[1] eq 'option') {          } elsif ($token->{tag_name} eq 'optgroup') {
6315                  !!!cp ('t274');            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6316                  ## As if </option>              !!!cp ('t274');
6317                  pop @{$self->{open_elements}};              ## As if </option>
6318                } else {              pop @{$self->{open_elements}};
6319                  !!!cp ('t275');            } else {
6320                }              !!!cp ('t275');
6321              }
6322    
6323                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6324                  !!!cp ('t276');              !!!cp ('t276');
6325                  ## As if </optgroup>              ## As if </optgroup>
6326                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6327                } else {            } else {
6328                  !!!cp ('t277');              !!!cp ('t277');
6329                }            }
6330    
6331                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6332                !!!next-token;            !!!nack ('t277.1');
6333                redo B;            !!!next-token;
6334          } elsif ($token->{tag_name} eq 'select' or            next B;
6335                   $token->{tag_name} eq 'input' or          } elsif ({
6336                       select => 1, input => 1, textarea => 1,
6337                     }->{$token->{tag_name}} or
6338                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6339                    {                    {
6340                     caption => 1, table => 1,                     caption => 1, table => 1,
# Line 5115  sub _tree_construction_main ($) { Line 6342  sub _tree_construction_main ($) {
6342                     tr => 1, td => 1, th => 1,                     tr => 1, td => 1, th => 1,
6343                    }->{$token->{tag_name}})) {                    }->{$token->{tag_name}})) {
6344            ## TODO: The type below is not good - <select> is replaced by </select>            ## TODO: The type below is not good - <select> is replaced by </select>
6345            !!!parse-error (type => 'not closed:select', token => $token);            !!!parse-error (type => 'not closed', text => 'select',
6346                              token => $token);
6347            ## NOTE: As if the token were </select> (<select> case) or            ## NOTE: As if the token were </select> (<select> case) or
6348            ## as if there were </select> (otherwise).            ## as if there were </select> (otherwise).
6349                ## have an element in table scope            ## have an element in table scope
6350                my $i;            my $i;
6351                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6352                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6353                  if ($node->[1] eq 'select') {              if ($node->[1] & SELECT_EL) {
6354                    !!!cp ('t278');                !!!cp ('t278');
6355                    $i = $_;                $i = $_;
6356                    last INSCOPE;                last INSCOPE;
6357                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6358                            table => 1, html => 1,                !!!cp ('t279');
6359                           }->{$node->[1]}) {                last INSCOPE;
6360                    !!!cp ('t279');              }
6361                    last INSCOPE;            } # INSCOPE
6362                  }            unless (defined $i) {
6363                } # INSCOPE              !!!cp ('t280');
6364                unless (defined $i) {              !!!parse-error (type => 'unmatched end tag',
6365                  !!!cp ('t280');                              text => 'select', token => $token);
6366                  !!!parse-error (type => 'unmatched end tag:select', token => $token);              ## Ignore the token
6367                  ## Ignore the token              !!!nack ('t280.1');
6368                  !!!next-token;              !!!next-token;
6369                  redo B;              next B;
6370                }            }
6371                                
6372                !!!cp ('t281');            !!!cp ('t281');
6373                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6374    
6375                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6376    
6377            if ($token->{tag_name} eq 'select') {            if ($token->{tag_name} eq 'select') {
6378              !!!cp ('t281.2');              !!!nack ('t281.2');
6379              !!!next-token;              !!!next-token;
6380              redo B;              next B;
6381            } else {            } else {
6382              !!!cp ('t281.1');              !!!cp ('t281.1');
6383                !!!ack-later;
6384              ## Reprocess the token.              ## Reprocess the token.
6385              redo B;              next B;
6386            }            }
6387          } else {          } else {
6388            !!!cp ('t282');            !!!cp ('t282');
6389            !!!parse-error (type => 'in select:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'in select',
6390                              text => $token->{tag_name}, token => $token);
6391            ## Ignore the token            ## Ignore the token
6392              !!!nack ('t282.1');
6393            !!!next-token;            !!!next-token;
6394            redo B;            next B;
6395          }          }
6396        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6397              if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
6398                if ($self->{open_elements}->[-1]->[1] eq 'option' and            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
6399                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
6400                  !!!cp ('t283');              !!!cp ('t283');
6401                  ## As if </option>              ## As if </option>
6402                  splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
6403                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6404                  !!!cp ('t284');              !!!cp ('t284');
6405                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6406                } else {            } else {
6407                  !!!cp ('t285');              !!!cp ('t285');
6408                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
6409                  ## Ignore the token                              text => $token->{tag_name}, token => $token);
6410                }              ## Ignore the token
6411                !!!next-token;            }
6412                redo B;            !!!nack ('t285.1');
6413              } elsif ($token->{tag_name} eq 'option') {            !!!next-token;
6414                if ($self->{open_elements}->[-1]->[1] eq 'option') {            next B;
6415                  !!!cp ('t286');          } elsif ($token->{tag_name} eq 'option') {
6416                  pop @{$self->{open_elements}};            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6417                } else {              !!!cp ('t286');
6418                  !!!cp ('t287');              pop @{$self->{open_elements}};
6419                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            } else {
6420                  ## Ignore the token              !!!cp ('t287');
6421                }              !!!parse-error (type => 'unmatched end tag',
6422                !!!next-token;                              text => $token->{tag_name}, token => $token);
6423                redo B;              ## Ignore the token
6424              } elsif ($token->{tag_name} eq 'select') {            }
6425                ## have an element in table scope            !!!nack ('t287.1');
6426                my $i;            !!!next-token;
6427                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            next B;
6428                  my $node = $self->{open_elements}->[$_];          } elsif ($token->{tag_name} eq 'select') {
6429                  if ($node->[1] eq $token->{tag_name}) {            ## have an element in table scope
6430                    !!!cp ('t288');            my $i;
6431                    $i = $_;            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6432                    last INSCOPE;              my $node = $self->{open_elements}->[$_];
6433                  } elsif ({              if ($node->[1] & SELECT_EL) {
6434                            table => 1, html => 1,                !!!cp ('t288');
6435                           }->{$node->[1]}) {                $i = $_;
6436                    !!!cp ('t289');                last INSCOPE;
6437                    last INSCOPE;              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6438                  }                !!!cp ('t289');
6439                } # INSCOPE                last INSCOPE;
6440                unless (defined $i) {              }
6441                  !!!cp ('t290');            } # INSCOPE
6442                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            unless (defined $i) {
6443                  ## Ignore the token              !!!cp ('t290');
6444                  !!!next-token;              !!!parse-error (type => 'unmatched end tag',
6445                  redo B;                              text => $token->{tag_name}, token => $token);
6446                }              ## Ignore the token
6447                !!!nack ('t290.1');
6448                !!!next-token;
6449                next B;
6450              }
6451                                
6452                !!!cp ('t291');            !!!cp ('t291');
6453                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6454    
6455                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6456    
6457                !!!next-token;            !!!nack ('t291.1');
6458                redo B;            !!!next-token;
6459              next B;
6460          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6461                   {                   {
6462                    caption => 1, table => 1, tbody => 1,                    caption => 1, table => 1, tbody => 1,
6463                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6464                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
6465  ## TODO: The following is wrong?  ## TODO: The following is wrong?
6466                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
6467                              text => $token->{tag_name}, token => $token);
6468                                
6469                ## have an element in table scope            ## have an element in table scope
6470                my $i;            my $i;
6471                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6472                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6473                  if ($node->[1] eq $token->{tag_name}) {              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6474                    !!!cp ('t292');                !!!cp ('t292');
6475                    $i = $_;                $i = $_;
6476                    last INSCOPE;                last INSCOPE;
6477                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6478                            table => 1, html => 1,                !!!cp ('t293');
6479                           }->{$node->[1]}) {                last INSCOPE;
6480                    !!!cp ('t293');              }
6481                    last INSCOPE;            } # INSCOPE
6482                  }            unless (defined $i) {
6483                } # INSCOPE              !!!cp ('t294');
6484                unless (defined $i) {              ## Ignore the token
6485                  !!!cp ('t294');              !!!nack ('t294.1');
6486                  ## Ignore the token              !!!next-token;
6487                  !!!next-token;              next B;
6488                  redo B;            }
               }  
6489                                
6490                ## As if </select>            ## As if </select>
6491                ## have an element in table scope            ## have an element in table scope
6492                undef $i;            undef $i;
6493                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6494                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6495                  if ($node->[1] eq 'select') {              if ($node->[1] & SELECT_EL) {
6496                    !!!cp ('t295');                !!!cp ('t295');
6497                    $i = $_;                $i = $_;
6498                    last INSCOPE;                last INSCOPE;
6499                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6500  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
6501                    !!!cp ('t296');                !!!cp ('t296');
6502                    last INSCOPE;                last INSCOPE;
6503                  }              }
6504                } # INSCOPE            } # INSCOPE
6505                unless (defined $i) {            unless (defined $i) {
6506                  !!!cp ('t297');              !!!cp ('t297');
6507  ## TODO: The following error type is correct?  ## TODO: The following error type is correct?
6508                  !!!parse-error (type => 'unmatched end tag:select', token => $token);              !!!parse-error (type => 'unmatched end tag',
6509                  ## Ignore the </select> token                              text => 'select', token => $token);
6510                  !!!next-token; ## TODO: ok?              ## Ignore the </select> token
6511                  redo B;              !!!nack ('t297.1');
6512                }              !!!next-token; ## TODO: ok?
6513                next B;
6514              }
6515                                
6516                !!!cp ('t298');            !!!cp ('t298');
6517                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6518    
6519                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6520    
6521                ## reprocess            !!!ack-later;
6522                redo B;            ## reprocess
6523              next B;
6524          } else {          } else {
6525            !!!cp ('t299');            !!!cp ('t299');
6526            !!!parse-error (type => 'in select:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'in select:/',
6527                              text => $token->{tag_name}, token => $token);
6528            ## Ignore the token            ## Ignore the token
6529              !!!nack ('t299.3');
6530            !!!next-token;            !!!next-token;
6531            redo B;            next B;
6532          }          }
6533        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6534          unless ($self->{open_elements}->[-1]->[1] eq 'html' and          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6535                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
6536            !!!cp ('t299.1');            !!!cp ('t299.1');
6537            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 5307  sub _tree_construction_main ($) { Line 6546  sub _tree_construction_main ($) {
6546        }        }
6547      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6548        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6549          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6550            my $data = $1;            my $data = $1;
6551            ## As if in body            ## As if in body
6552            $reconstruct_active_formatting_elements->($insert_to_current);            $reconstruct_active_formatting_elements->($insert_to_current);
# Line 5317  sub _tree_construction_main ($) { Line 6556  sub _tree_construction_main ($) {
6556            unless (length $token->{data}) {            unless (length $token->{data}) {
6557              !!!cp ('t300');              !!!cp ('t300');
6558              !!!next-token;              !!!next-token;
6559              redo B;              next B;
6560            }            }
6561          }          }
6562                    
6563          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6564            !!!cp ('t301');            !!!cp ('t301');
6565            !!!parse-error (type => 'after html:#character', token => $token);            !!!parse-error (type => 'after html:#text', token => $token);
6566              #
           ## Reprocess in the "after body" insertion mode.  
6567          } else {          } else {
6568            !!!cp ('t302');            !!!cp ('t302');
6569              ## "after body" insertion mode
6570              !!!parse-error (type => 'after body:#text', token => $token);
6571              #
6572          }          }
           
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:#character', token => $token);  
6573    
6574          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6575          ## reprocess          ## reprocess
6576          redo B;          next B;
6577        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6578          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6579            !!!cp ('t303');            !!!cp ('t303');
6580            !!!parse-error (type => 'after html:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'after html',
6581                                        text => $token->{tag_name}, token => $token);
6582            ## Reprocess in the "after body" insertion mode.            #
6583          } else {          } else {
6584            !!!cp ('t304');            !!!cp ('t304');
6585              ## "after body" insertion mode
6586              !!!parse-error (type => 'after body',
6587                              text => $token->{tag_name}, token => $token);
6588              #
6589          }          }
6590    
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:'.$token->{tag_name}, token => $token);  
   
6591          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6592            !!!ack-later;
6593          ## reprocess          ## reprocess
6594          redo B;          next B;
6595        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6596          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6597            !!!cp ('t305');            !!!cp ('t305');
6598            !!!parse-error (type => 'after html:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'after html:/',
6599                              text => $token->{tag_name}, token => $token);
6600                        
6601            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6602            ## Reprocess in the "after body" insertion mode.            ## Reprocess.
6603              next B;
6604          } else {          } else {
6605            !!!cp ('t306');            !!!cp ('t306');
6606          }          }
# Line 5367  sub _tree_construction_main ($) { Line 6609  sub _tree_construction_main ($) {
6609          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
6610            if (defined $self->{inner_html_node}) {            if (defined $self->{inner_html_node}) {
6611              !!!cp ('t307');              !!!cp ('t307');
6612              !!!parse-error (type => 'unmatched end tag:html', token => $token);              !!!parse-error (type => 'unmatched end tag',
6613                                text => 'html', token => $token);
6614              ## Ignore the token              ## Ignore the token
6615              !!!next-token;              !!!next-token;
6616              redo B;              next B;
6617            } else {            } else {
6618              !!!cp ('t308');              !!!cp ('t308');
6619              $self->{insertion_mode} = AFTER_HTML_BODY_IM;              $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6620              !!!next-token;              !!!next-token;
6621              redo B;              next B;
6622            }            }
6623          } else {          } else {
6624            !!!cp ('t309');            !!!cp ('t309');
6625            !!!parse-error (type => 'after body:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'after body:/',
6626                              text => $token->{tag_name}, token => $token);
6627    
6628            $self->{insertion_mode} = IN_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6629            ## reprocess            ## reprocess
6630            redo B;            next B;
6631          }          }
6632        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6633          !!!cp ('t309.2');          !!!cp ('t309.2');
# Line 5394  sub _tree_construction_main ($) { Line 6638  sub _tree_construction_main ($) {
6638        }        }
6639      } elsif ($self->{insertion_mode} & FRAME_IMS) {      } elsif ($self->{insertion_mode} & FRAME_IMS) {
6640        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6641          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6642            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6643                        
6644            unless (length $token->{data}) {            unless (length $token->{data}) {
6645              !!!cp ('t310');              !!!cp ('t310');
6646              !!!next-token;              !!!next-token;
6647              redo B;              next B;
6648            }            }
6649          }          }
6650                    
6651          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6652            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6653              !!!cp ('t311');              !!!cp ('t311');
6654              !!!parse-error (type => 'in frameset:#character', token => $token);              !!!parse-error (type => 'in frameset:#text', token => $token);
6655            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6656              !!!cp ('t312');              !!!cp ('t312');
6657              !!!parse-error (type => 'after frameset:#character', token => $token);              !!!parse-error (type => 'after frameset:#text', token => $token);
6658            } else { # "after html frameset"            } else { # "after after frameset"
6659              !!!cp ('t313');              !!!cp ('t313');
6660              !!!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);  
6661            }            }
6662                        
6663            ## Ignore the token.            ## Ignore the token.
# Line 5428  sub _tree_construction_main ($) { Line 6668  sub _tree_construction_main ($) {
6668              !!!cp ('t315');              !!!cp ('t315');
6669              !!!next-token;              !!!next-token;
6670            }            }
6671            redo B;            next B;
6672          }          }
6673                    
6674          die qq[$0: Character "$token->{data}"];          die qq[$0: Character "$token->{data}"];
6675        } 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');  
         }  
   
6676          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6677              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6678            !!!cp ('t318');            !!!cp ('t318');
6679            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6680              !!!nack ('t318.1');
6681            !!!next-token;            !!!next-token;
6682            redo B;            next B;
6683          } elsif ($token->{tag_name} eq 'frame' and          } elsif ($token->{tag_name} eq 'frame' and
6684                   $self->{insertion_mode} == IN_FRAMESET_IM) {                   $self->{insertion_mode} == IN_FRAMESET_IM) {
6685            !!!cp ('t319');            !!!cp ('t319');
6686            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6687            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
6688              !!!ack ('t319.1');
6689            !!!next-token;            !!!next-token;
6690            redo B;            next B;
6691          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
6692            !!!cp ('t320');            !!!cp ('t320');
6693            ## NOTE: As if in body.            ## NOTE: As if in head.
6694            $parse_rcdata->(CDATA_CONTENT_MODEL);            $parse_rcdata->(CDATA_CONTENT_MODEL);
6695            redo B;            next B;
6696    
6697              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6698              ## has no parse error.
6699          } else {          } else {
6700            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6701              !!!cp ('t321');              !!!cp ('t321');
6702              !!!parse-error (type => 'in frameset:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'in frameset',
6703            } else {                              text => $token->{tag_name}, token => $token);
6704              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6705              !!!cp ('t322');              !!!cp ('t322');
6706              !!!parse-error (type => 'after frameset:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'after frameset',
6707                                text => $token->{tag_name}, token => $token);
6708              } else { # "after after frameset"
6709                !!!cp ('t322.2');
6710                !!!parse-error (type => 'after after frameset',
6711                                text => $token->{tag_name}, token => $token);
6712            }            }
6713            ## Ignore the token            ## Ignore the token
6714              !!!nack ('t322.1');
6715            !!!next-token;            !!!next-token;
6716            redo B;            next B;
6717          }          }
6718        } 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');  
         }  
   
6719          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6720              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6721            if ($self->{open_elements}->[-1]->[1] eq 'html' and            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6722                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
6723              !!!cp ('t325');              !!!cp ('t325');
6724              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
6725                                text => $token->{tag_name}, token => $token);
6726              ## Ignore the token              ## Ignore the token
6727              !!!next-token;              !!!next-token;
6728            } else {            } else {
# Line 5499  sub _tree_construction_main ($) { Line 6732  sub _tree_construction_main ($) {
6732            }            }
6733    
6734            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
6735                $self->{open_elements}->[-1]->[1] ne 'frameset') {                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
6736              !!!cp ('t327');              !!!cp ('t327');
6737              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
6738            } else {            } else {
6739              !!!cp ('t328');              !!!cp ('t328');
6740            }            }
6741            redo B;            next B;
6742          } elsif ($token->{tag_name} eq 'html' and          } elsif ($token->{tag_name} eq 'html' and
6743                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6744            !!!cp ('t329');            !!!cp ('t329');
6745            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6746            !!!next-token;            !!!next-token;
6747            redo B;            next B;
6748          } else {          } else {
6749            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6750              !!!cp ('t330');              !!!cp ('t330');
6751              !!!parse-error (type => 'in frameset:/'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'in frameset:/',
6752            } else {                              text => $token->{tag_name}, token => $token);
6753              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6754                !!!cp ('t330.1');
6755                !!!parse-error (type => 'after frameset:/',
6756                                text => $token->{tag_name}, token => $token);
6757              } else { # "after after html"
6758              !!!cp ('t331');              !!!cp ('t331');
6759              !!!parse-error (type => 'after frameset:/'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'after after frameset:/',
6760                                text => $token->{tag_name}, token => $token);
6761            }            }
6762            ## Ignore the token            ## Ignore the token
6763            !!!next-token;            !!!next-token;
6764            redo B;            next B;
6765          }          }
6766        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6767          unless ($self->{open_elements}->[-1]->[1] eq 'html' and          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6768                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
6769            !!!cp ('t331.1');            !!!cp ('t331.1');
6770            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 5550  sub _tree_construction_main ($) { Line 6789  sub _tree_construction_main ($) {
6789          !!!cp ('t332');          !!!cp ('t332');
6790          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6791          $script_start_tag->();          $script_start_tag->();
6792          redo B;          next B;
6793        } elsif ($token->{tag_name} eq 'style') {        } elsif ($token->{tag_name} eq 'style') {
6794          !!!cp ('t333');          !!!cp ('t333');
6795          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6796          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
6797          redo B;          next B;
6798        } elsif ({        } elsif ({
6799                  base => 1, link => 1,                  base => 1, link => 1,
6800                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
# Line 5563  sub _tree_construction_main ($) { Line 6802  sub _tree_construction_main ($) {
6802          ## 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
6803          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6804          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
6805            !!!ack ('t334.1');
6806          !!!next-token;          !!!next-token;
6807          redo B;          next B;
6808        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
6809          ## 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
6810          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6811          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.
6812    
6813          unless ($self->{confident}) {          unless ($self->{confident}) {
6814            if ($token->{attributes}->{charset}) { ## TODO: And if supported            if ($token->{attributes}->{charset}) {
6815              !!!cp ('t335');              !!!cp ('t335');
6816                ## NOTE: Whether the encoding is supported or not is handled
6817                ## in the {change_encoding} callback.
6818              $self->{change_encoding}              $self->{change_encoding}
6819                  ->($self, $token->{attributes}->{charset}->{value}, $token);                  ->($self, $token->{attributes}->{charset}->{value}, $token);
6820                            
# Line 5581  sub _tree_construction_main ($) { Line 6823  sub _tree_construction_main ($) {
6823                                       $token->{attributes}->{charset}                                       $token->{attributes}->{charset}
6824                                           ->{has_reference});                                           ->{has_reference});
6825            } elsif ($token->{attributes}->{content}) {            } elsif ($token->{attributes}->{content}) {
             ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
6826              if ($token->{attributes}->{content}->{value}              if ($token->{attributes}->{content}->{value}
6827                  =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6828                      [\x09-\x0D\x20]*=                      [\x09\x0A\x0C\x0D\x20]*=
6829                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                      [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6830                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                      ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6831                       /x) {
6832                !!!cp ('t336');                !!!cp ('t336');
6833                  ## NOTE: Whether the encoding is supported or not is handled
6834                  ## in the {change_encoding} callback.
6835                $self->{change_encoding}                $self->{change_encoding}
6836                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6837                $meta_el->[0]->get_attribute_node_ns (undef, 'content')                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
# Line 5613  sub _tree_construction_main ($) { Line 6857  sub _tree_construction_main ($) {
6857            }            }
6858          }          }
6859    
6860            !!!ack ('t338.1');
6861          !!!next-token;          !!!next-token;
6862          redo B;          next B;
6863        } elsif ($token->{tag_name} eq 'title') {        } elsif ($token->{tag_name} eq 'title') {
6864          !!!cp ('t341');          !!!cp ('t341');
6865          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6866          $parse_rcdata->(RCDATA_CONTENT_MODEL);          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6867          redo B;          next B;
6868        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
6869          !!!parse-error (type => 'in body:body', token => $token);          !!!parse-error (type => 'in body', text => 'body', token => $token);
6870                                
6871          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
6872              $self->{open_elements}->[1]->[1] ne 'body') {              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
6873            !!!cp ('t342');            !!!cp ('t342');
6874            ## Ignore the token            ## Ignore the token
6875          } else {          } else {
# Line 5638  sub _tree_construction_main ($) { Line 6883  sub _tree_construction_main ($) {
6883              }              }
6884            }            }
6885          }          }
6886            !!!nack ('t343.1');
6887          !!!next-token;          !!!next-token;
6888          redo B;          next B;
6889        } elsif ({        } elsif ({
6890                  address => 1, blockquote => 1, center => 1, dir => 1,                  address => 1, blockquote => 1, center => 1, dir => 1,
6891                  div => 1, dl => 1, fieldset => 1,                  div => 1, dl => 1, fieldset => 1,
# Line 5654  sub _tree_construction_main ($) { Line 6900  sub _tree_construction_main ($) {
6900            !!!cp ('t350');            !!!cp ('t350');
6901            !!!parse-error (type => 'in form:form', token => $token);            !!!parse-error (type => 'in form:form', token => $token);
6902            ## Ignore the token            ## Ignore the token
6903              !!!nack ('t350.1');
6904            !!!next-token;            !!!next-token;
6905            redo B;            next B;
6906          }          }
6907    
6908          ## has a p element in scope          ## has a p element in scope
6909          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
6910            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
6911              !!!cp ('t344');              !!!cp ('t344');
6912              !!!back-token;              !!!back-token; # <form>
6913              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
6914                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
6915              redo B;              next B;
6916            } elsif ({            } elsif ($_->[1] & SCOPING_EL) {
                     applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
6917              !!!cp ('t345');              !!!cp ('t345');
6918              last INSCOPE;              last INSCOPE;
6919            }            }
# Line 5677  sub _tree_construction_main ($) { Line 6921  sub _tree_construction_main ($) {
6921                        
6922          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6923          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
6924              !!!nack ('t346.1');
6925            !!!next-token;            !!!next-token;
6926            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6927              $token->{data} =~ s/^\x0A//;              $token->{data} =~ s/^\x0A//;
# Line 5693  sub _tree_construction_main ($) { Line 6938  sub _tree_construction_main ($) {
6938            !!!cp ('t347.1');            !!!cp ('t347.1');
6939            $self->{form_element} = $self->{open_elements}->[-1]->[0];            $self->{form_element} = $self->{open_elements}->[-1]->[0];
6940    
6941              !!!nack ('t347.2');
6942            !!!next-token;            !!!next-token;
6943          } elsif ($token->{tag_name} eq 'table') {          } elsif ($token->{tag_name} eq 'table') {
6944            !!!cp ('t382');            !!!cp ('t382');
# Line 5700  sub _tree_construction_main ($) { Line 6946  sub _tree_construction_main ($) {
6946                        
6947            $self->{insertion_mode} = IN_TABLE_IM;            $self->{insertion_mode} = IN_TABLE_IM;
6948    
6949              !!!nack ('t382.1');
6950            !!!next-token;            !!!next-token;
6951          } elsif ($token->{tag_name} eq 'hr') {          } elsif ($token->{tag_name} eq 'hr') {
6952            !!!cp ('t386');            !!!cp ('t386');
6953            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
6954                    
6955              !!!nack ('t386.1');
6956            !!!next-token;            !!!next-token;
6957          } else {          } else {
6958            !!!cp ('t347');            !!!nack ('t347.1');
6959            !!!next-token;            !!!next-token;
6960          }          }
6961          redo B;          next B;
6962        } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {        } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {
6963          ## has a p element in scope          ## has a p element in scope
6964          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
6965            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
6966              !!!cp ('t353');              !!!cp ('t353');
6967              !!!back-token;              !!!back-token; # <x>
6968              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
6969                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
6970              redo B;              next B;
6971            } elsif ({            } elsif ($_->[1] & SCOPING_EL) {
                     applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
6972              !!!cp ('t354');              !!!cp ('t354');
6973              last INSCOPE;              last INSCOPE;
6974            }            }
6975          } # INSCOPE          } # INSCOPE
6976    
6977            ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
6978              ## Interpreted as <li><foo/></li><li/> (non-conforming)
6979              ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
6980              ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
6981              ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
6982              ## object (Fx)
6983              ## Generate non-tree (non-conforming)
6984              ## basefont (IE7 (where basefont is non-void)), center (IE),
6985              ## form (IE), hn (IE)
6986            ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
6987              ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
6988              ## div (Fx, S)
6989                        
6990          ## Step 1          ## Step 1
6991          my $i = -1;          my $i = -1;
# Line 5737  sub _tree_construction_main ($) { Line 6995  sub _tree_construction_main ($) {
6995                            dd => {dt => 1, dd => 1}}->{$token->{tag_name}};                            dd => {dt => 1, dd => 1}}->{$token->{tag_name}};
6996          LI: {          LI: {
6997            ## Step 2            ## Step 2
6998            if ($li_or_dtdd->{$node->[1]}) {            if ($li_or_dtdd->{$node->[0]->manakai_local_name}) {
6999              if ($i != -1) {              if ($i != -1) {
7000                !!!cp ('t355');                !!!cp ('t355');
7001                !!!parse-error (type => 'end tag missing:'.                !!!parse-error (type => 'not closed',
7002                                $self->{open_elements}->[-1]->[1], token => $token);                                text => $self->{open_elements}->[-1]->[0]
7003                                      ->manakai_local_name,
7004                                  token => $token);
7005              } else {              } else {
7006                !!!cp ('t356');                !!!cp ('t356');
7007              }              }
# Line 5752  sub _tree_construction_main ($) { Line 7012  sub _tree_construction_main ($) {
7012            }            }
7013                        
7014            ## Step 3            ## Step 3
7015            if (not $formatting_category->{$node->[1]} and            if (not ($node->[1] & FORMATTING_EL) and
7016                #not $phrasing_category->{$node->[1]} and                #not $phrasing_category->{$node->[1]} and
7017                ($special_category->{$node->[1]} or                ($node->[1] & SPECIAL_EL or
7018                 $scoping_category->{$node->[1]}) and                 $node->[1] & SCOPING_EL) and
7019                $node->[1] ne 'address' and $node->[1] ne 'div') {                not ($node->[1] & ADDRESS_EL) and
7020                  not ($node->[1] & DIV_EL)) {
7021              !!!cp ('t358');              !!!cp ('t358');
7022              last LI;              last LI;
7023            }            }
# Line 5769  sub _tree_construction_main ($) { Line 7030  sub _tree_construction_main ($) {
7030          } # LI          } # LI
7031                        
7032          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7033            !!!nack ('t359.1');
7034          !!!next-token;          !!!next-token;
7035          redo B;          next B;
7036        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
7037          ## has a p element in scope          ## has a p element in scope
7038          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7039            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7040              !!!cp ('t367');              !!!cp ('t367');
7041              !!!back-token;              !!!back-token; # <plaintext>
7042              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7043                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
7044              redo B;              next B;
7045            } elsif ({            } elsif ($_->[1] & SCOPING_EL) {
                     applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
7046              !!!cp ('t368');              !!!cp ('t368');
7047              last INSCOPE;              last INSCOPE;
7048            }            }
# Line 5793  sub _tree_construction_main ($) { Line 7052  sub _tree_construction_main ($) {
7052                        
7053          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7054                        
7055            !!!nack ('t368.1');
7056          !!!next-token;          !!!next-token;
7057          redo B;          next B;
7058        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
7059          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7060            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
7061            if ($node->[1] eq 'a') {            if ($node->[1] & A_EL) {
7062              !!!cp ('t371');              !!!cp ('t371');
7063              !!!parse-error (type => 'in a:a', token => $token);              !!!parse-error (type => 'in a:a', token => $token);
7064                            
7065              !!!back-token;              !!!back-token; # <a>
7066              $token = {type => END_TAG_TOKEN, tag_name => 'a',              $token = {type => END_TAG_TOKEN, tag_name => 'a',
7067                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
7068              $formatting_end_tag->($token);              $formatting_end_tag->($token);
# Line 5833  sub _tree_construction_main ($) { Line 7093  sub _tree_construction_main ($) {
7093          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7094          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7095    
7096            !!!nack ('t374.1');
7097          !!!next-token;          !!!next-token;
7098          redo B;          next B;
7099        } elsif ($token->{tag_name} eq 'nobr') {        } elsif ($token->{tag_name} eq 'nobr') {
7100          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7101    
7102          ## has a |nobr| element in scope          ## has a |nobr| element in scope
7103          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7104            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7105            if ($node->[1] eq 'nobr') {            if ($node->[1] & NOBR_EL) {
7106              !!!cp ('t376');              !!!cp ('t376');
7107              !!!parse-error (type => 'in nobr:nobr', token => $token);              !!!parse-error (type => 'in nobr:nobr', token => $token);
7108              !!!back-token;              !!!back-token; # <nobr>
7109              $token = {type => END_TAG_TOKEN, tag_name => 'nobr',              $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7110                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
7111              redo B;              next B;
7112            } 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]}) {  
7113              !!!cp ('t377');              !!!cp ('t377');
7114              last INSCOPE;              last INSCOPE;
7115            }            }
# Line 5860  sub _tree_construction_main ($) { Line 7118  sub _tree_construction_main ($) {
7118          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7119          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7120                    
7121            !!!nack ('t377.1');
7122          !!!next-token;          !!!next-token;
7123          redo B;          next B;
7124        } elsif ($token->{tag_name} eq 'button') {        } elsif ($token->{tag_name} eq 'button') {
7125          ## has a button element in scope          ## has a button element in scope
7126          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7127            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7128            if ($node->[1] eq 'button') {            if ($node->[1] & BUTTON_EL) {
7129              !!!cp ('t378');              !!!cp ('t378');
7130              !!!parse-error (type => 'in button:button', token => $token);              !!!parse-error (type => 'in button:button', token => $token);
7131              !!!back-token;              !!!back-token; # <button>
7132              $token = {type => END_TAG_TOKEN, tag_name => 'button',              $token = {type => END_TAG_TOKEN, tag_name => 'button',
7133                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
7134              redo B;              next B;
7135            } 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]}) {  
7136              !!!cp ('t379');              !!!cp ('t379');
7137              last INSCOPE;              last INSCOPE;
7138            }            }
# Line 5890  sub _tree_construction_main ($) { Line 7146  sub _tree_construction_main ($) {
7146    
7147          push @$active_formatting_elements, ['#marker', ''];          push @$active_formatting_elements, ['#marker', ''];
7148    
7149            !!!nack ('t379.1');
7150          !!!next-token;          !!!next-token;
7151          redo B;          next B;
7152        } elsif ({        } elsif ({
7153                  xmp => 1,                  xmp => 1,
7154                  iframe => 1,                  iframe => 1,
7155                  noembed => 1,                  noembed => 1,
7156                  noframes => 1,                  noframes => 1, ## NOTE: This is an "as if in head" code clone.
7157                  noscript => 0, ## TODO: 1 if scripting is enabled                  noscript => 0, ## TODO: 1 if scripting is enabled
7158                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7159          if ($token->{tag_name} eq 'xmp') {          if ($token->{tag_name} eq 'xmp') {
# Line 5907  sub _tree_construction_main ($) { Line 7164  sub _tree_construction_main ($) {
7164          }          }
7165          ## NOTE: There is an "as if in body" code clone.          ## NOTE: There is an "as if in body" code clone.
7166          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
7167          redo B;          next B;
7168        } elsif ($token->{tag_name} eq 'isindex') {        } elsif ($token->{tag_name} eq 'isindex') {
7169          !!!parse-error (type => 'isindex', token => $token);          !!!parse-error (type => 'isindex', token => $token);
7170                    
7171          if (defined $self->{form_element}) {          if (defined $self->{form_element}) {
7172            !!!cp ('t389');            !!!cp ('t389');
7173            ## Ignore the token            ## Ignore the token
7174              !!!nack ('t389'); ## NOTE: Not acknowledged.
7175            !!!next-token;            !!!next-token;
7176            redo B;            next B;
7177          } else {          } else {
7178              !!!ack ('t391.1');
7179    
7180            my $at = $token->{attributes};            my $at = $token->{attributes};
7181            my $form_attrs;            my $form_attrs;
7182            $form_attrs->{action} = $at->{action} if $at->{action};            $form_attrs->{action} = $at->{action} if $at->{action};
# Line 5938  sub _tree_construction_main ($) { Line 7198  sub _tree_construction_main ($) {
7198            if ($prompt_attr) {            if ($prompt_attr) {
7199              !!!cp ('t390');              !!!cp ('t390');
7200              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7201                             line => $token->{line}, column => $token->{column}};                             #line => $token->{line}, column => $token->{column},
7202                              };
7203            } else {            } else {
7204              !!!cp ('t391');              !!!cp ('t391');
7205              push @tokens, {type => CHARACTER_TOKEN,              push @tokens, {type => CHARACTER_TOKEN,
7206                             data => 'This is a searchable index. Insert your search keywords here: ',                             data => 'This is a searchable index. Insert your search keywords here: ',
7207                             line => $token->{line}, column => $token->{column}}; # SHOULD                             #line => $token->{line}, column => $token->{column},
7208                              }; # SHOULD
7209              ## TODO: make this configurable              ## TODO: make this configurable
7210            }            }
7211            push @tokens,            push @tokens,
# Line 5958  sub _tree_construction_main ($) { Line 7220  sub _tree_construction_main ($) {
7220                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
7221                          {type => END_TAG_TOKEN, tag_name => 'form',                          {type => END_TAG_TOKEN, tag_name => 'form',
7222                           line => $token->{line}, column => $token->{column}};                           line => $token->{line}, column => $token->{column}};
           $token = shift @tokens;  
7223            !!!back-token (@tokens);            !!!back-token (@tokens);
7224            redo B;            !!!next-token;
7225              next B;
7226          }          }
7227        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
7228          my $tag_name = $token->{tag_name};          my $tag_name = $token->{tag_name};
7229          my $el;          my $el;
7230          !!!create-element ($el, $token->{tag_name}, $token->{attributes}, $token);          !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
7231                    
7232          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
7233          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
# Line 5974  sub _tree_construction_main ($) { Line 7236  sub _tree_construction_main ($) {
7236          $insert->($el);          $insert->($el);
7237                    
7238          my $text = '';          my $text = '';
7239            !!!nack ('t392.1');
7240          !!!next-token;          !!!next-token;
7241          if ($token->{type} == CHARACTER_TOKEN) {          if ($token->{type} == CHARACTER_TOKEN) {
7242            $token->{data} =~ s/^\x0A//;            $token->{data} =~ s/^\x0A//;
# Line 6004  sub _tree_construction_main ($) { Line 7267  sub _tree_construction_main ($) {
7267            ## Ignore the token            ## Ignore the token
7268          } else {          } else {
7269            !!!cp ('t398');            !!!cp ('t398');
7270            !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token);            !!!parse-error (type => 'in RCDATA:#eof', token => $token);
7271          }          }
7272          !!!next-token;          !!!next-token;
7273            next B;
7274          } elsif ($token->{tag_name} eq 'rt' or
7275                   $token->{tag_name} eq 'rp') {
7276            ## has a |ruby| element in scope
7277            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7278              my $node = $self->{open_elements}->[$_];
7279              if ($node->[1] & RUBY_EL) {
7280                !!!cp ('t398.1');
7281                ## generate implied end tags
7282                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7283                  !!!cp ('t398.2');
7284                  pop @{$self->{open_elements}};
7285                }
7286                unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7287                  !!!cp ('t398.3');
7288                  !!!parse-error (type => 'not closed',
7289                                  text => $self->{open_elements}->[-1]->[0]
7290                                      ->manakai_local_name,
7291                                  token => $token);
7292                  pop @{$self->{open_elements}}
7293                      while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
7294                }
7295                last INSCOPE;
7296              } elsif ($node->[1] & SCOPING_EL) {
7297                !!!cp ('t398.4');
7298                last INSCOPE;
7299              }
7300            } # INSCOPE
7301    
7302            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7303    
7304            !!!nack ('t398.5');
7305            !!!next-token;
7306          redo B;          redo B;
7307          } elsif ($token->{tag_name} eq 'math' or
7308                   $token->{tag_name} eq 'svg') {
7309            $reconstruct_active_formatting_elements->($insert_to_current);
7310    
7311            ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7312    
7313            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7314    
7315            ## "adjust foreign attributes" - done in insert-element-f
7316            
7317            !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7318            
7319            if ($self->{self_closing}) {
7320              pop @{$self->{open_elements}};
7321              !!!ack ('t398.1');
7322            } else {
7323              !!!cp ('t398.2');
7324              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7325              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7326              ## mode, "in body" (not "in foreign content") secondary insertion
7327              ## mode, maybe.
7328            }
7329    
7330            !!!next-token;
7331            next B;
7332        } elsif ({        } elsif ({
7333                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
7334                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1, option => 1, optgroup => 1,
# Line 6015  sub _tree_construction_main ($) { Line 7336  sub _tree_construction_main ($) {
7336                  thead => 1, tr => 1,                  thead => 1, tr => 1,
7337                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7338          !!!cp ('t401');          !!!cp ('t401');
7339          !!!parse-error (type => 'in body:'.$token->{tag_name}, token => $token);          !!!parse-error (type => 'in body',
7340                            text => $token->{tag_name}, token => $token);
7341          ## Ignore the token          ## Ignore the token
7342            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7343          !!!next-token;          !!!next-token;
7344          redo B;          next B;
7345                    
7346          ## ISSUE: An issue on HTML5 new elements in the spec.          ## ISSUE: An issue on HTML5 new elements in the spec.
7347        } else {        } else {
# Line 6040  sub _tree_construction_main ($) { Line 7363  sub _tree_construction_main ($) {
7363              }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
7364            !!!cp ('t380');            !!!cp ('t380');
7365            push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
7366              !!!nack ('t380.1');
7367          } elsif ({          } elsif ({
7368                    b => 1, big => 1, em => 1, font => 1, i => 1,                    b => 1, big => 1, em => 1, font => 1, i => 1,
7369                    s => 1, small => 1, strile => 1,                    s => 1, small => 1, strike => 1,
7370                    strong => 1, tt => 1, u => 1,                    strong => 1, tt => 1, u => 1,
7371                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
7372            !!!cp ('t375');            !!!cp ('t375');
7373            push @$active_formatting_elements, $self->{open_elements}->[-1];            push @$active_formatting_elements, $self->{open_elements}->[-1];
7374              !!!nack ('t375.1');
7375          } elsif ($token->{tag_name} eq 'input') {          } elsif ($token->{tag_name} eq 'input') {
7376            !!!cp ('t388');            !!!cp ('t388');
7377            ## TODO: associate with $self->{form_element} if defined            ## TODO: associate with $self->{form_element} if defined
7378            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
7379              !!!ack ('t388.2');
7380          } elsif ({          } elsif ({
7381                    area => 1, basefont => 1, bgsound => 1, br => 1,                    area => 1, basefont => 1, bgsound => 1, br => 1,
7382                    embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                    embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,
# Line 6058  sub _tree_construction_main ($) { Line 7384  sub _tree_construction_main ($) {
7384                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
7385            !!!cp ('t388.1');            !!!cp ('t388.1');
7386            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
7387              !!!ack ('t388.3');
7388          } elsif ($token->{tag_name} eq 'select') {          } elsif ($token->{tag_name} eq 'select') {
7389            ## TODO: associate with $self->{form_element} if defined            ## TODO: associate with $self->{form_element} if defined
7390                    
# Line 6070  sub _tree_construction_main ($) { Line 7397  sub _tree_construction_main ($) {
7397              !!!cp ('t400.2');              !!!cp ('t400.2');
7398              $self->{insertion_mode} = IN_SELECT_IM;              $self->{insertion_mode} = IN_SELECT_IM;
7399            }            }
7400              !!!nack ('t400.3');
7401          } else {          } else {
7402            !!!cp ('t402');            !!!nack ('t402');
7403          }          }
7404                    
7405          !!!next-token;          !!!next-token;
7406          redo B;          next B;
7407        }        }
7408      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
7409        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
# Line 6083  sub _tree_construction_main ($) { Line 7411  sub _tree_construction_main ($) {
7411          my $i;          my $i;
7412          INSCOPE: {          INSCOPE: {
7413            for (reverse @{$self->{open_elements}}) {            for (reverse @{$self->{open_elements}}) {
7414              if ($_->[1] eq 'body') {              if ($_->[1] & BODY_EL) {
7415                !!!cp ('t405');                !!!cp ('t405');
7416                $i = $_;                $i = $_;
7417                last INSCOPE;                last INSCOPE;
7418              } elsif ({              } elsif ($_->[1] & SCOPING_EL) {
                       applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                       button => 1, marquee => 1, object => 1, html => 1,  
                      }->{$_->[1]}) {  
7419                !!!cp ('t405.1');                !!!cp ('t405.1');
7420                last;                last;
7421              }              }
7422            }            }
7423    
7424            !!!parse-error (type => 'start tag not allowed',            !!!parse-error (type => 'start tag not allowed',
7425                            value => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
7426            ## NOTE: Ignore the token.            ## NOTE: Ignore the token.
7427            !!!next-token;            !!!next-token;
7428            redo B;            next B;
7429          } # INSCOPE          } # INSCOPE
7430    
7431          for (@{$self->{open_elements}}) {          for (@{$self->{open_elements}}) {
7432            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]}) {  
7433              !!!cp ('t403');              !!!cp ('t403');
7434              !!!parse-error (type => 'not closed:'.$_->[1], token => $token);              !!!parse-error (type => 'not closed',
7435                                text => $_->[0]->manakai_local_name,
7436                                token => $token);
7437              last;              last;
7438            } else {            } else {
7439              !!!cp ('t404');              !!!cp ('t404');
# Line 6119  sub _tree_construction_main ($) { Line 7442  sub _tree_construction_main ($) {
7442    
7443          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
7444          !!!next-token;          !!!next-token;
7445          redo B;          next B;
7446        } elsif ($token->{tag_name} eq 'html') {        } elsif ($token->{tag_name} eq 'html') {
7447          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
7448            ## up-to-date, though it has same effect as speced.
7449            if (@{$self->{open_elements}} > 1 and
7450                $self->{open_elements}->[1]->[1] & BODY_EL) {
7451            ## ISSUE: There is an issue in the spec.            ## ISSUE: There is an issue in the spec.
7452            if ($self->{open_elements}->[-1]->[1] ne 'body') {            unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7453              !!!cp ('t406');              !!!cp ('t406');
7454              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1], token => $token);              !!!parse-error (type => 'not closed',
7455                                text => $self->{open_elements}->[1]->[0]
7456                                    ->manakai_local_name,
7457                                token => $token);
7458            } else {            } else {
7459              !!!cp ('t407');              !!!cp ('t407');
7460            }            }
7461            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = AFTER_BODY_IM;
7462            ## reprocess            ## reprocess
7463            redo B;            next B;
7464          } else {          } else {
7465            !!!cp ('t408');            !!!cp ('t408');
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            ## Ignore the token            ## Ignore the token
7469            !!!next-token;            !!!next-token;
7470            redo B;            next B;
7471          }          }
7472        } elsif ({        } elsif ({
7473                  address => 1, blockquote => 1, center => 1, dir => 1,                  address => 1, blockquote => 1, center => 1, dir => 1,
# Line 6150  sub _tree_construction_main ($) { Line 7480  sub _tree_construction_main ($) {
7480          my $i;          my $i;
7481          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7482            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7483            if ($node->[1] eq $token->{tag_name}) {            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7484              !!!cp ('t410');              !!!cp ('t410');
7485              $i = $_;              $i = $_;
7486              last INSCOPE;              last INSCOPE;
7487            } 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]}) {  
7488              !!!cp ('t411');              !!!cp ('t411');
7489              last INSCOPE;              last INSCOPE;
7490            }            }
# Line 6165  sub _tree_construction_main ($) { Line 7492  sub _tree_construction_main ($) {
7492    
7493          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7494            !!!cp ('t413');            !!!cp ('t413');
7495            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7496                              text => $token->{tag_name}, token => $token);
7497              ## NOTE: Ignore the token.
7498          } else {          } else {
7499            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7500            while ({            while ({
7501                      ## END_TAG_OPTIONAL_EL
7502                    dd => ($token->{tag_name} ne 'dd'),                    dd => ($token->{tag_name} ne 'dd'),
7503                    dt => ($token->{tag_name} ne 'dt'),                    dt => ($token->{tag_name} ne 'dt'),
7504                    li => ($token->{tag_name} ne 'li'),                    li => ($token->{tag_name} ne 'li'),
7505                    p => 1,                    p => 1,
7506                   }->{$self->{open_elements}->[-1]->[1]}) {                    rt => 1,
7507                      rp => 1,
7508                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7509              !!!cp ('t409');              !!!cp ('t409');
7510              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7511            }            }
7512    
7513            ## Step 2.            ## Step 2.
7514            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7515                      ne $token->{tag_name}) {
7516              !!!cp ('t412');              !!!cp ('t412');
7517              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);              !!!parse-error (type => 'not closed',
7518                                text => $self->{open_elements}->[-1]->[0]
7519                                    ->manakai_local_name,
7520                                token => $token);
7521            } else {            } else {
7522              !!!cp ('t414');              !!!cp ('t414');
7523            }            }
# Line 6196  sub _tree_construction_main ($) { Line 7532  sub _tree_construction_main ($) {
7532                }->{$token->{tag_name}};                }->{$token->{tag_name}};
7533          }          }
7534          !!!next-token;          !!!next-token;
7535          redo B;          next B;
7536        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
7537          undef $self->{form_element};          undef $self->{form_element};
7538    
# Line 6204  sub _tree_construction_main ($) { Line 7540  sub _tree_construction_main ($) {
7540          my $i;          my $i;
7541          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7542            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7543            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] & FORM_EL) {
7544              !!!cp ('t418');              !!!cp ('t418');
7545              $i = $_;              $i = $_;
7546              last INSCOPE;              last INSCOPE;
7547            } 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]}) {  
7548              !!!cp ('t419');              !!!cp ('t419');
7549              last INSCOPE;              last INSCOPE;
7550            }            }
# Line 6219  sub _tree_construction_main ($) { Line 7552  sub _tree_construction_main ($) {
7552    
7553          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7554            !!!cp ('t421');            !!!cp ('t421');
7555            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7556                              text => $token->{tag_name}, token => $token);
7557              ## NOTE: Ignore the token.
7558          } else {          } else {
7559            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7560            while ({            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                   dd => 1, dt => 1, li => 1, p => 1,  
                  }->{$self->{open_elements}->[-1]->[1]}) {  
7561              !!!cp ('t417');              !!!cp ('t417');
7562              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7563            }            }
7564                        
7565            ## Step 2.            ## Step 2.
7566            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7567                      ne $token->{tag_name}) {
7568              !!!cp ('t417.1');              !!!cp ('t417.1');
7569              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);              !!!parse-error (type => 'not closed',
7570                                text => $self->{open_elements}->[-1]->[0]
7571                                    ->manakai_local_name,
7572                                token => $token);
7573            } else {            } else {
7574              !!!cp ('t420');              !!!cp ('t420');
7575            }              }  
# Line 6242  sub _tree_construction_main ($) { Line 7579  sub _tree_construction_main ($) {
7579          }          }
7580    
7581          !!!next-token;          !!!next-token;
7582          redo B;          next B;
7583        } elsif ({        } elsif ({
7584                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7585                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
# Line 6250  sub _tree_construction_main ($) { Line 7587  sub _tree_construction_main ($) {
7587          my $i;          my $i;
7588          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7589            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7590            if ({            if ($node->[1] & HEADING_EL) {
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
7591              !!!cp ('t423');              !!!cp ('t423');
7592              $i = $_;              $i = $_;
7593              last INSCOPE;              last INSCOPE;
7594            } 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]}) {  
7595              !!!cp ('t424');              !!!cp ('t424');
7596              last INSCOPE;              last INSCOPE;
7597            }            }
# Line 6267  sub _tree_construction_main ($) { Line 7599  sub _tree_construction_main ($) {
7599    
7600          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7601            !!!cp ('t425.1');            !!!cp ('t425.1');
7602            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7603                              text => $token->{tag_name}, token => $token);
7604              ## NOTE: Ignore the token.
7605          } else {          } else {
7606            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7607            while ({            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                   dd => 1, dt => 1, li => 1, p => 1,  
                  }->{$self->{open_elements}->[-1]->[1]}) {  
7608              !!!cp ('t422');              !!!cp ('t422');
7609              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7610            }            }
7611                        
7612            ## Step 2.            ## Step 2.
7613            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7614                      ne $token->{tag_name}) {
7615              !!!cp ('t425');              !!!cp ('t425');
7616              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
7617                                text => $token->{tag_name}, token => $token);
7618            } else {            } else {
7619              !!!cp ('t426');              !!!cp ('t426');
7620            }            }
# Line 6290  sub _tree_construction_main ($) { Line 7624  sub _tree_construction_main ($) {
7624          }          }
7625                    
7626          !!!next-token;          !!!next-token;
7627          redo B;          next B;
7628        } elsif ($token->{tag_name} eq 'p') {        } elsif ($token->{tag_name} eq 'p') {
7629          ## has an element in scope          ## has an element in scope
7630          my $i;          my $i;
7631          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7632            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7633            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] & P_EL) {
7634              !!!cp ('t410.1');              !!!cp ('t410.1');
7635              $i = $_;              $i = $_;
7636              last INSCOPE;              last INSCOPE;
7637            } 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]}) {  
7638              !!!cp ('t411.1');              !!!cp ('t411.1');
7639              last INSCOPE;              last INSCOPE;
7640            }            }
7641          } # INSCOPE          } # INSCOPE
7642    
7643          if (defined $i) {          if (defined $i) {
7644            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7645                      ne $token->{tag_name}) {
7646              !!!cp ('t412.1');              !!!cp ('t412.1');
7647              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);              !!!parse-error (type => 'not closed',
7648                                text => $self->{open_elements}->[-1]->[0]
7649                                    ->manakai_local_name,
7650                                token => $token);
7651            } else {            } else {
7652              !!!cp ('t414.1');              !!!cp ('t414.1');
7653            }            }
# Line 6320  sub _tree_construction_main ($) { Line 7655  sub _tree_construction_main ($) {
7655            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
7656          } else {          } else {
7657            !!!cp ('t413.1');            !!!cp ('t413.1');
7658            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7659                              text => $token->{tag_name}, token => $token);
7660    
7661            !!!cp ('t415.1');            !!!cp ('t415.1');
7662            ## As if <p>, then reprocess the current token            ## As if <p>, then reprocess the current token
7663            my $el;            my $el;
7664            !!!create-element ($el, 'p',, $token);            !!!create-element ($el, $HTML_NS, 'p',, $token);
7665            $insert->($el);            $insert->($el);
7666            ## NOTE: Not inserted into |$self->{open_elements}|.            ## NOTE: Not inserted into |$self->{open_elements}|.
7667          }          }
7668    
7669          !!!next-token;          !!!next-token;
7670          redo B;          next B;
7671        } elsif ({        } elsif ({
7672                  a => 1,                  a => 1,
7673                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
7674                  nobr => 1, s => 1, small => 1, strile => 1,                  nobr => 1, s => 1, small => 1, strike => 1,
7675                  strong => 1, tt => 1, u => 1,                  strong => 1, tt => 1, u => 1,
7676                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7677          !!!cp ('t427');          !!!cp ('t427');
7678          $formatting_end_tag->($token);          $formatting_end_tag->($token);
7679          redo B;          next B;
7680        } elsif ($token->{tag_name} eq 'br') {        } elsif ($token->{tag_name} eq 'br') {
7681          !!!cp ('t428');          !!!cp ('t428');
7682          !!!parse-error (type => 'unmatched end tag:br', token => $token);          !!!parse-error (type => 'unmatched end tag',
7683                            text => 'br', token => $token);
7684    
7685          ## As if <br>          ## As if <br>
7686          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7687                    
7688          my $el;          my $el;
7689          !!!create-element ($el, 'br',, $token);          !!!create-element ($el, $HTML_NS, 'br',, $token);
7690          $insert->($el);          $insert->($el);
7691                    
7692          ## Ignore the token.          ## Ignore the token.
7693          !!!next-token;          !!!next-token;
7694          redo B;          next B;
7695        } elsif ({        } elsif ({
7696                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
7697                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1, option => 1, optgroup => 1,
# Line 6368  sub _tree_construction_main ($) { Line 7705  sub _tree_construction_main ($) {
7705                  noscript => 0, ## TODO: if scripting is enabled                  noscript => 0, ## TODO: if scripting is enabled
7706                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7707          !!!cp ('t429');          !!!cp ('t429');
7708          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);          !!!parse-error (type => 'unmatched end tag',
7709                            text => $token->{tag_name}, token => $token);
7710          ## Ignore the token          ## Ignore the token
7711          !!!next-token;          !!!next-token;
7712          redo B;          next B;
7713                    
7714          ## ISSUE: Issue on HTML5 new elements in spec          ## ISSUE: Issue on HTML5 new elements in spec
7715                    
# Line 6382  sub _tree_construction_main ($) { Line 7720  sub _tree_construction_main ($) {
7720    
7721          ## Step 2          ## Step 2
7722          S2: {          S2: {
7723            if ($node->[1] eq $token->{tag_name}) {            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7724              ## Step 1              ## Step 1
7725              ## generate implied end tags              ## generate implied end tags
7726              while ({              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                     dd => 1, dt => 1, li => 1, p => 1,  
                    }->{$self->{open_elements}->[-1]->[1]}) {  
7727                !!!cp ('t430');                !!!cp ('t430');
7728                ## ISSUE: Can this case be reached?                ## NOTE: |<ruby><rt></ruby>|.
7729                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7730                  ## which seems wrong.
7731                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
7732                  $node_i++;
7733              }              }
7734                    
7735              ## Step 2              ## Step 2
7736              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7737                        ne $token->{tag_name}) {
7738                !!!cp ('t431');                !!!cp ('t431');
7739                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
7740                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                !!!parse-error (type => 'not closed',
7741                                  text => $self->{open_elements}->[-1]->[0]
7742                                      ->manakai_local_name,
7743                                  token => $token);
7744              } else {              } else {
7745                !!!cp ('t432');                !!!cp ('t432');
7746              }              }
7747                            
7748              ## Step 3              ## Step 3
7749              splice @{$self->{open_elements}}, $node_i;              splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7750    
7751              !!!next-token;              !!!next-token;
7752              last S2;              last S2;
7753            } else {            } else {
7754              ## Step 3              ## Step 3
7755              if (not $formatting_category->{$node->[1]} and              if (not ($node->[1] & FORMATTING_EL) and
7756                  #not $phrasing_category->{$node->[1]} and                  #not $phrasing_category->{$node->[1]} and
7757                  ($special_category->{$node->[1]} or                  ($node->[1] & SPECIAL_EL or
7758                   $scoping_category->{$node->[1]})) {                   $node->[1] & SCOPING_EL)) {
7759                !!!cp ('t433');                !!!cp ('t433');
7760                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
7761                                  text => $token->{tag_name}, token => $token);
7762                ## Ignore the token                ## Ignore the token
7763                !!!next-token;                !!!next-token;
7764                last S2;                last S2;
             }  
7765    
7766                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7767                  ## 9.27, "a" is a child of <dd> (conforming).  In
7768                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7769                  ## "a" is a child of both <body> and <dd>.
7770                }
7771                
7772              !!!cp ('t434');              !!!cp ('t434');
7773            }            }
7774                        
# Line 6430  sub _tree_construction_main ($) { Line 7779  sub _tree_construction_main ($) {
7779            ## Step 5;            ## Step 5;
7780            redo S2;            redo S2;
7781          } # S2          } # S2
7782          redo B;          next B;
7783        }        }
7784      }      }
7785      redo B;      next B;
7786      } continue { # B
7787        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
7788          ## NOTE: The code below is executed in cases where it does not have
7789          ## to be, but it it is harmless even in those cases.
7790          ## has an element in scope
7791          INSCOPE: {
7792            for (reverse 0..$#{$self->{open_elements}}) {
7793              my $node = $self->{open_elements}->[$_];
7794              if ($node->[1] & FOREIGN_EL) {
7795                last INSCOPE;
7796              } elsif ($node->[1] & SCOPING_EL) {
7797                last;
7798              }
7799            }
7800            
7801            ## NOTE: No foreign element in scope.
7802            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
7803          } # INSCOPE
7804        }
7805    } # B    } # B
7806    
7807    ## Stop parsing # MUST    ## Stop parsing # MUST
# Line 6441  sub _tree_construction_main ($) { Line 7809  sub _tree_construction_main ($) {
7809    ## TODO: script stuffs    ## TODO: script stuffs
7810  } # _tree_construct_main  } # _tree_construct_main
7811    
7812  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
7813    my $class = shift;    my $class = shift;
7814    my $node = shift;    my $node = shift;
7815    my $s = \$_[0];    #my $s = \$_[0];
7816    my $onerror = $_[1];    my $onerror = $_[1];
7817      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
7818    
7819    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
7820    
# Line 6464  sub set_inner_html ($$$) { Line 7833  sub set_inner_html ($$$) {
7833      }      }
7834    
7835      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
7836      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
7837    } elsif ($nt == 1) {    } elsif ($nt == 1) {
7838      ## TODO: If non-html element      ## TODO: If non-html element
7839    
7840      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
7841    
7842    ## TODO: Support for $get_wrapper
7843    
7844      ## Step 1 # MUST      ## Step 1 # MUST
7845      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
7846      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 6479  sub set_inner_html ($$$) { Line 7850  sub set_inner_html ($$$) {
7850    
7851      ## Step 8 # MUST      ## Step 8 # MUST
7852      my $i = 0;      my $i = 0;
7853      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
7854      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
7855      $p->{set_next_char} = sub {      require Whatpm::Charset::DecodeHandle;
7856        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
7857        $input = $get_wrapper->($input);
7858        $p->{set_nc} = sub {
7859        my $self = shift;        my $self = shift;
7860    
7861        pop @{$self->{prev_char}};        my $char = '';
7862        unshift @{$self->{prev_char}}, $self->{next_char};        if (defined $self->{next_nc}) {
7863            $char = $self->{next_nc};
7864            delete $self->{next_nc};
7865            $self->{nc} = ord $char;
7866          } else {
7867            $self->{char_buffer} = '';
7868            $self->{char_buffer_pos} = 0;
7869            
7870            my $count = $input->manakai_read_until
7871                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
7872                 $self->{char_buffer_pos});
7873            if ($count) {
7874              $self->{line_prev} = $self->{line};
7875              $self->{column_prev} = $self->{column};
7876              $self->{column}++;
7877              $self->{nc}
7878                  = ord substr ($self->{char_buffer},
7879                                $self->{char_buffer_pos}++, 1);
7880              return;
7881            }
7882            
7883            if ($input->read ($char, 1)) {
7884              $self->{nc} = ord $char;
7885            } else {
7886              $self->{nc} = -1;
7887              return;
7888            }
7889          }
7890    
7891          ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
7892          $p->{column}++;
7893    
7894        $self->{next_char} = -1 and return if $i >= length $$s;        if ($self->{nc} == 0x000A) { # LF
7895        $self->{next_char} = ord substr $$s, $i++, 1;          $p->{line}++;
7896        $column++;          $p->{column} = 0;
   
       if ($self->{next_char} == 0x000A) { # LF  
         $line++;  
         $column = 0;  
7897          !!!cp ('i1');          !!!cp ('i1');
7898        } elsif ($self->{next_char} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
7899          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
7900          $self->{next_char} = 0x000A; # LF # MUST          my $next = '';
7901          $line++;          if ($input->read ($next, 1) and $next ne "\x0A") {
7902          $column = 0;            $self->{next_nc} = $next;
7903            }
7904            $self->{nc} = 0x000A; # LF # MUST
7905            $p->{line}++;
7906            $p->{column} = 0;
7907          !!!cp ('i2');          !!!cp ('i2');
7908        } 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  
7909          !!!cp ('i4');          !!!cp ('i4');
7910          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
7911          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
7912        }        }
7913      };      };
7914      $p->{prev_char} = [-1, -1, -1];  
7915      $p->{next_char} = -1;      $p->{read_until} = sub {
7916              #my ($scalar, $specials_range, $offset) = @_;
7917          return 0 if defined $p->{next_nc};
7918    
7919          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
7920          my $offset = $_[2] || 0;
7921          
7922          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
7923            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
7924            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
7925              substr ($_[0], $offset)
7926                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
7927              my $count = $+[0] - $-[0];
7928              if ($count) {
7929                $p->{column} += $count;
7930                $p->{char_buffer_pos} += $count;
7931                $p->{line_prev} = $p->{line};
7932                $p->{column_prev} = $p->{column} - 1;
7933                $p->{nc} = -1;
7934              }
7935              return $count;
7936            } else {
7937              return 0;
7938            }
7939          } else {
7940            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
7941            if ($count) {
7942              $p->{column} += $count;
7943              $p->{column_prev} += $count;
7944              $p->{nc} = -1;
7945            }
7946            return $count;
7947          }
7948        }; # $p->{read_until}
7949    
7950      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
7951        my (%opt) = @_;        my (%opt) = @_;
7952        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
7953          my $column = $opt{column};
7954          if (defined $opt{token} and defined $opt{token}->{line}) {
7955            $line = $opt{token}->{line};
7956            $column = $opt{token}->{column};
7957          }
7958          warn "Parse error ($opt{type}) at line $line column $column\n";
7959      };      };
7960      $p->{parse_error} = sub {      $p->{parse_error} = sub {
7961        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
7962      };      };
7963            
7964        my $char_onerror = sub {
7965          my (undef, $type, %opt) = @_;
7966          $ponerror->(layer => 'encode',
7967                      line => $p->{line}, column => $p->{column} + 1,
7968                      %opt, type => $type);
7969        }; # $char_onerror
7970        $input->onerror ($char_onerror);
7971    
7972      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
7973      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
7974    
# Line 6542  sub set_inner_html ($$$) { Line 7990  sub set_inner_html ($$$) {
7990          unless defined $p->{content_model};          unless defined $p->{content_model};
7991          ## ISSUE: What is "the name of the element"? local name?          ## ISSUE: What is "the name of the element"? local name?
7992    
7993      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
7994          ## TODO: Foreign element OK?
7995    
7996      ## Step 3      ## Step 3
7997      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
# Line 6552  sub set_inner_html ($$$) { Line 8001  sub set_inner_html ($$$) {
8001      $doc->append_child ($root);      $doc->append_child ($root);
8002    
8003      ## Step 5 # MUST      ## Step 5 # MUST
8004      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
8005    
8006      undef $p->{head_element};      undef $p->{head_element};
8007    
# Line 6598  sub set_inner_html ($$$) { Line 8047  sub set_inner_html ($$$) {
8047      ## ISSUE: mutation events?      ## ISSUE: mutation events?
8048    
8049      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
8050    
8051        delete $p->{parse_error}; # delete loop
8052    } else {    } else {
8053      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";
8054    }    }

Legend:
Removed from v.1.116  
changed lines
  Added in v.1.193

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24