/[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.127 by wakaba, Sat Apr 12 15:25:52 2008 UTC revision 1.194 by wakaba, Sat Oct 4 05:53:45 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;
 ## TODO: 1252 parse error (revision 1264)  
 ## TODO: 8859-11 = 874 (revision 1271)  
23    
24  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
25  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
# Line 47  sub MISC_SPECIAL_EL () { 0b1000000000000 Line 56  sub MISC_SPECIAL_EL () { 0b1000000000000
56  sub FOREIGN_EL () { 0b10000000000000000000000000 }  sub FOREIGN_EL () { 0b10000000000000000000000000 }
57  sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }  sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }
58  sub MML_AXML_EL () { 0b1000000000000000000000000000 }  sub MML_AXML_EL () { 0b1000000000000000000000000000 }
59    sub RUBY_EL () { 0b10000000000000000000000000000 }
60    sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }
61    
62  sub TABLE_ROWS_EL () {  sub TABLE_ROWS_EL () {
63    TABLE_EL |    TABLE_EL |
# Line 54  sub TABLE_ROWS_EL () { Line 65  sub TABLE_ROWS_EL () {
65    TABLE_ROW_GROUP_EL    TABLE_ROW_GROUP_EL
66  }  }
67    
68    ## NOTE: Used in "generate implied end tags" algorithm.
69    ## NOTE: There is a code where a modified version of
70    ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
71    ## implementation (search for the algorithm name).
72  sub END_TAG_OPTIONAL_EL () {  sub END_TAG_OPTIONAL_EL () {
73    DD_EL |    DD_EL |
74    DT_EL |    DT_EL |
75    LI_EL |    LI_EL |
76    P_EL    OPTION_EL |
77      OPTGROUP_EL |
78      P_EL |
79      RUBY_COMPONENT_EL
80  }  }
81    
82    ## NOTE: Used in </body> and EOF algorithms.
83  sub ALL_END_TAG_OPTIONAL_EL () {  sub ALL_END_TAG_OPTIONAL_EL () {
84    END_TAG_OPTIONAL_EL |    DD_EL |
85      DT_EL |
86      LI_EL |
87      P_EL |
88    
89    BODY_EL |    BODY_EL |
90    HTML_EL |    HTML_EL |
91    TABLE_CELL_EL |    TABLE_CELL_EL |
# Line 98  sub SPECIAL_EL () { Line 121  sub SPECIAL_EL () {
121    ADDRESS_EL |    ADDRESS_EL |
122    BODY_EL |    BODY_EL |
123    DIV_EL |    DIV_EL |
124    END_TAG_OPTIONAL_EL |  
125      DD_EL |
126      DT_EL |
127      LI_EL |
128      P_EL |
129    
130    FORM_EL |    FORM_EL |
131    FRAMESET_EL |    FRAMESET_EL |
132    HEADING_EL |    HEADING_EL |
# Line 115  my $el_category = { Line 143  my $el_category = {
143    address => ADDRESS_EL,    address => ADDRESS_EL,
144    applet => MISC_SCOPING_EL,    applet => MISC_SCOPING_EL,
145    area => MISC_SPECIAL_EL,    area => MISC_SPECIAL_EL,
146      article => MISC_SPECIAL_EL,
147      aside => MISC_SPECIAL_EL,
148    b => FORMATTING_EL,    b => FORMATTING_EL,
149    base => MISC_SPECIAL_EL,    base => MISC_SPECIAL_EL,
150    basefont => MISC_SPECIAL_EL,    basefont => MISC_SPECIAL_EL,
# Line 128  my $el_category = { Line 158  my $el_category = {
158    center => MISC_SPECIAL_EL,    center => MISC_SPECIAL_EL,
159    col => MISC_SPECIAL_EL,    col => MISC_SPECIAL_EL,
160    colgroup => MISC_SPECIAL_EL,    colgroup => MISC_SPECIAL_EL,
161      command => MISC_SPECIAL_EL,
162      datagrid => MISC_SPECIAL_EL,
163    dd => DD_EL,    dd => DD_EL,
164      details => MISC_SPECIAL_EL,
165      dialog => MISC_SPECIAL_EL,
166    dir => MISC_SPECIAL_EL,    dir => MISC_SPECIAL_EL,
167    div => DIV_EL,    div => DIV_EL,
168    dl => MISC_SPECIAL_EL,    dl => MISC_SPECIAL_EL,
169    dt => DT_EL,    dt => DT_EL,
170    em => FORMATTING_EL,    em => FORMATTING_EL,
171    embed => MISC_SPECIAL_EL,    embed => MISC_SPECIAL_EL,
172      eventsource => MISC_SPECIAL_EL,
173    fieldset => MISC_SPECIAL_EL,    fieldset => MISC_SPECIAL_EL,
174      figure => MISC_SPECIAL_EL,
175    font => FORMATTING_EL,    font => FORMATTING_EL,
176      footer => MISC_SPECIAL_EL,
177    form => FORM_EL,    form => FORM_EL,
178    frame => MISC_SPECIAL_EL,    frame => MISC_SPECIAL_EL,
179    frameset => FRAMESET_EL,    frameset => FRAMESET_EL,
# Line 147  my $el_category = { Line 184  my $el_category = {
184    h5 => HEADING_EL,    h5 => HEADING_EL,
185    h6 => HEADING_EL,    h6 => HEADING_EL,
186    head => MISC_SPECIAL_EL,    head => MISC_SPECIAL_EL,
187      header => MISC_SPECIAL_EL,
188    hr => MISC_SPECIAL_EL,    hr => MISC_SPECIAL_EL,
189    html => HTML_EL,    html => HTML_EL,
190    i => FORMATTING_EL,    i => FORMATTING_EL,
191    iframe => MISC_SPECIAL_EL,    iframe => MISC_SPECIAL_EL,
192    img => MISC_SPECIAL_EL,    img => MISC_SPECIAL_EL,
193      #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
194    input => MISC_SPECIAL_EL,    input => MISC_SPECIAL_EL,
195    isindex => MISC_SPECIAL_EL,    isindex => MISC_SPECIAL_EL,
196    li => LI_EL,    li => LI_EL,
# Line 160  my $el_category = { Line 199  my $el_category = {
199    marquee => MISC_SCOPING_EL,    marquee => MISC_SCOPING_EL,
200    menu => MISC_SPECIAL_EL,    menu => MISC_SPECIAL_EL,
201    meta => MISC_SPECIAL_EL,    meta => MISC_SPECIAL_EL,
202      nav => MISC_SPECIAL_EL,
203    nobr => NOBR_EL | FORMATTING_EL,    nobr => NOBR_EL | FORMATTING_EL,
204    noembed => MISC_SPECIAL_EL,    noembed => MISC_SPECIAL_EL,
205    noframes => MISC_SPECIAL_EL,    noframes => MISC_SPECIAL_EL,
# Line 172  my $el_category = { Line 212  my $el_category = {
212    param => MISC_SPECIAL_EL,    param => MISC_SPECIAL_EL,
213    plaintext => MISC_SPECIAL_EL,    plaintext => MISC_SPECIAL_EL,
214    pre => MISC_SPECIAL_EL,    pre => MISC_SPECIAL_EL,
215      rp => RUBY_COMPONENT_EL,
216      rt => RUBY_COMPONENT_EL,
217      ruby => RUBY_EL,
218    s => FORMATTING_EL,    s => FORMATTING_EL,
219    script => MISC_SPECIAL_EL,    script => MISC_SPECIAL_EL,
220    select => SELECT_EL,    select => SELECT_EL,
221      section => MISC_SPECIAL_EL,
222    small => FORMATTING_EL,    small => FORMATTING_EL,
223    spacer => MISC_SPECIAL_EL,    spacer => MISC_SPECIAL_EL,
224    strike => FORMATTING_EL,    strike => FORMATTING_EL,
# Line 205  my $el_category_f = { Line 249  my $el_category_f = {
249      mtext => FOREIGN_FLOW_CONTENT_EL,      mtext => FOREIGN_FLOW_CONTENT_EL,
250    },    },
251    $SVG_NS => {    $SVG_NS => {
252      foreignobject => FOREIGN_FLOW_CONTENT_EL, ## TODO: case      foreignObject => FOREIGN_FLOW_CONTENT_EL,
253      desc => FOREIGN_FLOW_CONTENT_EL,      desc => FOREIGN_FLOW_CONTENT_EL,
254      title => FOREIGN_FLOW_CONTENT_EL,      title => FOREIGN_FLOW_CONTENT_EL,
255    },    },
256    ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.    ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
257  };  };
258    
259  my $c1_entity_char = {  my $svg_attr_name = {
260      attributename => 'attributeName',
261      attributetype => 'attributeType',
262      basefrequency => 'baseFrequency',
263      baseprofile => 'baseProfile',
264      calcmode => 'calcMode',
265      clippathunits => 'clipPathUnits',
266      contentscripttype => 'contentScriptType',
267      contentstyletype => 'contentStyleType',
268      diffuseconstant => 'diffuseConstant',
269      edgemode => 'edgeMode',
270      externalresourcesrequired => 'externalResourcesRequired',
271      filterres => 'filterRes',
272      filterunits => 'filterUnits',
273      glyphref => 'glyphRef',
274      gradienttransform => 'gradientTransform',
275      gradientunits => 'gradientUnits',
276      kernelmatrix => 'kernelMatrix',
277      kernelunitlength => 'kernelUnitLength',
278      keypoints => 'keyPoints',
279      keysplines => 'keySplines',
280      keytimes => 'keyTimes',
281      lengthadjust => 'lengthAdjust',
282      limitingconeangle => 'limitingConeAngle',
283      markerheight => 'markerHeight',
284      markerunits => 'markerUnits',
285      markerwidth => 'markerWidth',
286      maskcontentunits => 'maskContentUnits',
287      maskunits => 'maskUnits',
288      numoctaves => 'numOctaves',
289      pathlength => 'pathLength',
290      patterncontentunits => 'patternContentUnits',
291      patterntransform => 'patternTransform',
292      patternunits => 'patternUnits',
293      pointsatx => 'pointsAtX',
294      pointsaty => 'pointsAtY',
295      pointsatz => 'pointsAtZ',
296      preservealpha => 'preserveAlpha',
297      preserveaspectratio => 'preserveAspectRatio',
298      primitiveunits => 'primitiveUnits',
299      refx => 'refX',
300      refy => 'refY',
301      repeatcount => 'repeatCount',
302      repeatdur => 'repeatDur',
303      requiredextensions => 'requiredExtensions',
304      requiredfeatures => 'requiredFeatures',
305      specularconstant => 'specularConstant',
306      specularexponent => 'specularExponent',
307      spreadmethod => 'spreadMethod',
308      startoffset => 'startOffset',
309      stddeviation => 'stdDeviation',
310      stitchtiles => 'stitchTiles',
311      surfacescale => 'surfaceScale',
312      systemlanguage => 'systemLanguage',
313      tablevalues => 'tableValues',
314      targetx => 'targetX',
315      targety => 'targetY',
316      textlength => 'textLength',
317      viewbox => 'viewBox',
318      viewtarget => 'viewTarget',
319      xchannelselector => 'xChannelSelector',
320      ychannelselector => 'yChannelSelector',
321      zoomandpan => 'zoomAndPan',
322    };
323    
324    my $foreign_attr_xname = {
325      'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
326      'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
327      'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
328      'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
329      'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
330      'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
331      'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
332      'xml:base' => [$XML_NS, ['xml', 'base']],
333      'xml:lang' => [$XML_NS, ['xml', 'lang']],
334      'xml:space' => [$XML_NS, ['xml', 'space']],
335      'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
336      'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
337    };
338    
339    ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
340    
341    my $charref_map = {
342      0x0D => 0x000A,
343    0x80 => 0x20AC,    0x80 => 0x20AC,
344    0x81 => 0xFFFD,    0x81 => 0xFFFD,
345    0x82 => 0x201A,    0x82 => 0x201A,
# Line 245  my $c1_entity_char = { Line 372  my $c1_entity_char = {
372    0x9D => 0xFFFD,    0x9D => 0xFFFD,
373    0x9E => 0x017E,    0x9E => 0x017E,
374    0x9F => 0x0178,    0x9F => 0x0178,
375  }; # $c1_entity_char  }; # $charref_map
376    $charref_map->{$_} = 0xFFFD
377        for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
378            0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
379            0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
380            0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
381            0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
382            0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
383            0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
384    
385    ## TODO: Invoke the reset algorithm when a resettable element is
386    ## created (cf. HTML5 revision 2259).
387    
388  sub parse_byte_string ($$$$;$) {  sub parse_byte_string ($$$$;$) {
389      my $self = shift;
390      my $charset_name = shift;
391      open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
392      return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
393    } # parse_byte_string
394    
395    sub parse_byte_stream ($$$$;$$) {
396      # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
397    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
398    my $charset = shift;    my $charset_name = shift;
399    my $bytes_s = ref $_[0] ? $_[0] : \($_[0]);    my $byte_stream = $_[0];
400    my $s;  
401        my $onerror = $_[2] || sub {
402    if (defined $charset) {      my (%opt) = @_;
403      require Encode; ## TODO: decode(utf8) don't delete BOM      warn "Parse error ($opt{type})\n";
404      $s = \ (Encode::decode ($charset, $$bytes_s));    };
405      $self->{input_encoding} = lc $charset; ## TODO: normalize name    $self->{parse_error} = $onerror; # updated later by parse_char_string
406      $self->{confident} = 1;  
407    } else {    my $get_wrapper = $_[3] || sub ($) {
408      ## TODO: Implement HTML5 detection algorithm      return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
409      };
410    
411      ## HTML5 encoding sniffing algorithm
412      require Message::Charset::Info;
413      my $charset;
414      my $buffer;
415      my ($char_stream, $e_status);
416    
417      SNIFFING: {
418        ## NOTE: By setting |allow_fallback| option true when the
419        ## |get_decode_handle| method is invoked, we ignore what the HTML5
420        ## spec requires, i.e. unsupported encoding should be ignored.
421          ## TODO: We should not do this unless the parser is invoked
422          ## in the conformance checking mode, in which this behavior
423          ## would be useful.
424    
425        ## Step 1
426        if (defined $charset_name) {
427          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
428              ## TODO: Is this ok?  Transfer protocol's parameter should be
429              ## interpreted in its semantics?
430    
431          ($char_stream, $e_status) = $charset->get_decode_handle
432              ($byte_stream, allow_error_reporting => 1,
433               allow_fallback => 1);
434          if ($char_stream) {
435            $self->{confident} = 1;
436            last SNIFFING;
437          } else {
438            !!!parse-error (type => 'charset:not supported',
439                            layer => 'encode',
440                            line => 1, column => 1,
441                            value => $charset_name,
442                            level => $self->{level}->{uncertain});
443          }
444        }
445    
446        ## Step 2
447        my $byte_buffer = '';
448        for (1..1024) {
449          my $char = $byte_stream->getc;
450          last unless defined $char;
451          $byte_buffer .= $char;
452        } ## TODO: timeout
453    
454        ## Step 3
455        if ($byte_buffer =~ /^\xFE\xFF/) {
456          $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
457          ($char_stream, $e_status) = $charset->get_decode_handle
458              ($byte_stream, allow_error_reporting => 1,
459               allow_fallback => 1, byte_buffer => \$byte_buffer);
460          $self->{confident} = 1;
461          last SNIFFING;
462        } elsif ($byte_buffer =~ /^\xFF\xFE/) {
463          $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
464          ($char_stream, $e_status) = $charset->get_decode_handle
465              ($byte_stream, allow_error_reporting => 1,
466               allow_fallback => 1, byte_buffer => \$byte_buffer);
467          $self->{confident} = 1;
468          last SNIFFING;
469        } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
470          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
471          ($char_stream, $e_status) = $charset->get_decode_handle
472              ($byte_stream, allow_error_reporting => 1,
473               allow_fallback => 1, byte_buffer => \$byte_buffer);
474          $self->{confident} = 1;
475          last SNIFFING;
476        }
477    
478        ## Step 4
479        ## TODO: <meta charset>
480    
481        ## Step 5
482        ## TODO: from history
483    
484        ## Step 6
485      require Whatpm::Charset::UniversalCharDet;      require Whatpm::Charset::UniversalCharDet;
486      $charset = Whatpm::Charset::UniversalCharDet->detect_byte_string      $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
487          (substr ($$bytes_s, 0, 1024));          ($byte_buffer);
488      $charset ||= 'windows-1252';      if (defined $charset_name) {
489      $s = \ (Encode::decode ($charset, $$bytes_s));        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
490      $self->{input_encoding} = $charset;  
491          ## ISSUE: Unsupported encoding is not ignored according to the spec.
492          require Whatpm::Charset::DecodeHandle;
493          $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
494              ($byte_stream);
495          ($char_stream, $e_status) = $charset->get_decode_handle
496              ($buffer, allow_error_reporting => 1,
497               allow_fallback => 1, byte_buffer => \$byte_buffer);
498          if ($char_stream) {
499            $buffer->{buffer} = $byte_buffer;
500            !!!parse-error (type => 'sniffing:chardet',
501                            text => $charset_name,
502                            level => $self->{level}->{info},
503                            layer => 'encode',
504                            line => 1, column => 1);
505            $self->{confident} = 0;
506            last SNIFFING;
507          }
508        }
509    
510        ## Step 7: default
511        ## TODO: Make this configurable.
512        $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
513            ## NOTE: We choose |windows-1252| here, since |utf-8| should be
514            ## detectable in the step 6.
515        require Whatpm::Charset::DecodeHandle;
516        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
517            ($byte_stream);
518        ($char_stream, $e_status)
519            = $charset->get_decode_handle ($buffer,
520                                           allow_error_reporting => 1,
521                                           allow_fallback => 1,
522                                           byte_buffer => \$byte_buffer);
523        $buffer->{buffer} = $byte_buffer;
524        !!!parse-error (type => 'sniffing:default',
525                        text => 'windows-1252',
526                        level => $self->{level}->{info},
527                        line => 1, column => 1,
528                        layer => 'encode');
529      $self->{confident} = 0;      $self->{confident} = 0;
530      } # SNIFFING
531    
532      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
533        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
534        !!!parse-error (type => 'chardecode:fallback',
535                        #text => $self->{input_encoding},
536                        level => $self->{level}->{uncertain},
537                        line => 1, column => 1,
538                        layer => 'encode');
539      } elsif (not ($e_status &
540                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
541        $self->{input_encoding} = $charset->get_iana_name;
542        !!!parse-error (type => 'chardecode:no error',
543                        text => $self->{input_encoding},
544                        level => $self->{level}->{uncertain},
545                        line => 1, column => 1,
546                        layer => 'encode');
547      } else {
548        $self->{input_encoding} = $charset->get_iana_name;
549    }    }
550    
551    $self->{change_encoding} = sub {    $self->{change_encoding} = sub {
552      my $self = shift;      my $self = shift;
553      my $charset = lc shift;      $charset_name = shift;
554      my $token = shift;      my $token = shift;
     ## TODO: if $charset is supported  
     ## TODO: normalize charset name  
555    
556      ## "Change the encoding" algorithm:      $charset = Message::Charset::Info->get_by_html_name ($charset_name);
557        ($char_stream, $e_status) = $charset->get_decode_handle
558            ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
559             byte_buffer => \ $buffer->{buffer});
560        
561        if ($char_stream) { # if supported
562          ## "Change the encoding" algorithm:
563    
564      ## Step 1            ## Step 1    
565      if ($charset eq 'utf-16') { ## ISSUE: UTF-16BE -> UTF-8? UTF-16LE -> UTF-8?        if ($charset->{category} &
566        $charset = 'utf-8';            Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
567      }          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
568            ($char_stream, $e_status) = $charset->get_decode_handle
569                ($byte_stream,
570                 byte_buffer => \ $buffer->{buffer});
571          }
572          $charset_name = $charset->get_iana_name;
573          
574          ## Step 2
575          if (defined $self->{input_encoding} and
576              $self->{input_encoding} eq $charset_name) {
577            !!!parse-error (type => 'charset label:matching',
578                            text => $charset_name,
579                            level => $self->{level}->{info});
580            $self->{confident} = 1;
581            return;
582          }
583    
584      ## Step 2        !!!parse-error (type => 'charset label detected',
585      if (defined $self->{input_encoding} and                        text => $self->{input_encoding},
586          $self->{input_encoding} eq $charset) {                        value => $charset_name,
587        $self->{confident} = 1;                        level => $self->{level}->{warn},
588        return;                        token => $token);
589          
590          ## Step 3
591          # if (can) {
592            ## change the encoding on the fly.
593            #$self->{confident} = 1;
594            #return;
595          # }
596          
597          ## Step 4
598          throw Whatpm::HTML::RestartParser ();
599      }      }
600      }; # $self->{change_encoding}
601    
602      !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}.    my $char_onerror = sub {
603          ':'.$charset, level => 'w', token => $token);      my (undef, $type, %opt) = @_;
604        !!!parse-error (layer => 'encode',
605      ## Step 3                      line => $self->{line}, column => $self->{column} + 1,
606      # if (can) {                      %opt, type => $type);
607        ## change the encoding on the fly.      if ($opt{octets}) {
608        #$self->{confident} = 1;        ${$opt{octets}} = "\x{FFFD}"; # relacement character
609        #return;      }
610      # }    };
611    
612      ## Step 4    my $wrapped_char_stream = $get_wrapper->($char_stream);
613      throw Whatpm::HTML::RestartParser (charset => $charset);    $wrapped_char_stream->onerror ($char_onerror);
   }; # $self->{change_encoding}  
614    
615    my @args = @_; shift @args; # $s    my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
616    my $return;    my $return;
617    try {    try {
618      $return = $self->parse_char_string ($s, @args);        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
619    } catch Whatpm::HTML::RestartParser with {    } catch Whatpm::HTML::RestartParser with {
620      my $charset = shift->{charset};      ## NOTE: Invoked after {change_encoding}.
621      $s = \ (Encode::decode ($charset, $$bytes_s));      
622      $self->{input_encoding} = $charset; ## TODO: normalize      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
623          $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
624          !!!parse-error (type => 'chardecode:fallback',
625                          level => $self->{level}->{uncertain},
626                          #text => $self->{input_encoding},
627                          line => 1, column => 1,
628                          layer => 'encode');
629        } elsif (not ($e_status &
630                      Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
631          $self->{input_encoding} = $charset->get_iana_name;
632          !!!parse-error (type => 'chardecode:no error',
633                          text => $self->{input_encoding},
634                          level => $self->{level}->{uncertain},
635                          line => 1, column => 1,
636                          layer => 'encode');
637        } else {
638          $self->{input_encoding} = $charset->get_iana_name;
639        }
640      $self->{confident} = 1;      $self->{confident} = 1;
641      $return = $self->parse_char_string ($s, @args);  
642        $wrapped_char_stream = $get_wrapper->($char_stream);
643        $wrapped_char_stream->onerror ($char_onerror);
644    
645        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
646    };    };
647    return $return;    return $return;
648  } # parse_byte_string  } # parse_byte_stream
649    
650  ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM  ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
651  ## 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 327  sub parse_byte_string ($$$$;$) { Line 656  sub parse_byte_string ($$$$;$) {
656  ## 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
657  ## strip the BOM and never strip any ZWNBSP.  ## strip the BOM and never strip any ZWNBSP.
658    
659  *parse_char_string = \&parse_string;  sub parse_char_string ($$$;$$) {
660      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
661      my $self = shift;
662      my $s = ref $_[0] ? $_[0] : \($_[0]);
663      require Whatpm::Charset::DecodeHandle;
664      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
665      return $self->parse_char_stream ($input, @_[1..$#_]);
666    } # parse_char_string
667    *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
668    
669  sub parse_string ($$$;$) {  sub parse_char_stream ($$$;$$) {
670    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
671    my $s = ref $_[0] ? $_[0] : \($_[0]);    my $input = $_[0];
672    $self->{document} = $_[1];    $self->{document} = $_[1];
673    @{$self->{document}->child_nodes} = ();    @{$self->{document}->child_nodes} = ();
674    
# Line 340  sub parse_string ($$$;$) { Line 677  sub parse_string ($$$;$) {
677    $self->{confident} = 1 unless exists $self->{confident};    $self->{confident} = 1 unless exists $self->{confident};
678    $self->{document}->input_encoding ($self->{input_encoding})    $self->{document}->input_encoding ($self->{input_encoding})
679        if defined $self->{input_encoding};        if defined $self->{input_encoding};
680    ## TODO: |{input_encoding}| is needless?
681    
   my $i = 0;  
682    $self->{line_prev} = $self->{line} = 1;    $self->{line_prev} = $self->{line} = 1;
683    $self->{column_prev} = $self->{column} = 0;    $self->{column_prev} = -1;
684    $self->{set_next_char} = sub {    $self->{column} = 0;
685      $self->{set_nc} = sub {
686      my $self = shift;      my $self = shift;
687    
688      pop @{$self->{prev_char}};      my $char = '';
689      unshift @{$self->{prev_char}}, $self->{next_char};      if (defined $self->{next_nc}) {
690          $char = $self->{next_nc};
691          delete $self->{next_nc};
692          $self->{nc} = ord $char;
693        } else {
694          $self->{char_buffer} = '';
695          $self->{char_buffer_pos} = 0;
696    
697      $self->{next_char} = -1 and return if $i >= length $$s;        my $count = $input->manakai_read_until
698      $self->{next_char} = ord substr $$s, $i++, 1;           ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
699          if ($count) {
700            $self->{line_prev} = $self->{line};
701            $self->{column_prev} = $self->{column};
702            $self->{column}++;
703            $self->{nc}
704                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
705            return;
706          }
707    
708          if ($input->read ($char, 1)) {
709            $self->{nc} = ord $char;
710          } else {
711            $self->{nc} = -1;
712            return;
713          }
714        }
715    
716      ($self->{line_prev}, $self->{column_prev})      ($self->{line_prev}, $self->{column_prev})
717          = ($self->{line}, $self->{column});          = ($self->{line}, $self->{column});
718      $self->{column}++;      $self->{column}++;
719            
720      if ($self->{next_char} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
721          !!!cp ('j1');
722        $self->{line}++;        $self->{line}++;
723        $self->{column} = 0;        $self->{column} = 0;
724      } elsif ($self->{next_char} == 0x000D) { # CR      } elsif ($self->{nc} == 0x000D) { # CR
725        $i++ if substr ($$s, $i, 1) eq "\x0A";        !!!cp ('j2');
726        $self->{next_char} = 0x000A; # LF # MUST  ## TODO: support for abort/streaming
727          my $next = '';
728          if ($input->read ($next, 1) and $next ne "\x0A") {
729            $self->{next_nc} = $next;
730          }
731          $self->{nc} = 0x000A; # LF # MUST
732        $self->{line}++;        $self->{line}++;
733        $self->{column} = 0;        $self->{column} = 0;
734      } elsif ($self->{next_char} > 0x10FFFF) {      } elsif ($self->{nc} == 0x0000) { # NULL
735        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        !!!cp ('j4');
     } elsif ($self->{next_char} == 0x0000) { # NULL  
736        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
737        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
738      }      }
739    };    };
740    $self->{prev_char} = [-1, -1, -1];  
741    $self->{next_char} = -1;    $self->{read_until} = sub {
742        #my ($scalar, $specials_range, $offset) = @_;
743        return 0 if defined $self->{next_nc};
744    
745        my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
746        my $offset = $_[2] || 0;
747    
748        if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
749          pos ($self->{char_buffer}) = $self->{char_buffer_pos};
750          if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
751            substr ($_[0], $offset)
752                = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
753            my $count = $+[0] - $-[0];
754            if ($count) {
755              $self->{column} += $count;
756              $self->{char_buffer_pos} += $count;
757              $self->{line_prev} = $self->{line};
758              $self->{column_prev} = $self->{column} - 1;
759              $self->{nc} = -1;
760            }
761            return $count;
762          } else {
763            return 0;
764          }
765        } else {
766          my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
767          if ($count) {
768            $self->{column} += $count;
769            $self->{line_prev} = $self->{line};
770            $self->{column_prev} = $self->{column} - 1;
771            $self->{nc} = -1;
772          }
773          return $count;
774        }
775      }; # $self->{read_until}
776    
777    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
778      my (%opt) = @_;      my (%opt) = @_;
# Line 385  sub parse_string ($$$;$) { Line 784  sub parse_string ($$$;$) {
784      $onerror->(line => $self->{line}, column => $self->{column}, @_);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
785    };    };
786    
787      my $char_onerror = sub {
788        my (undef, $type, %opt) = @_;
789        !!!parse-error (layer => 'encode',
790                        line => $self->{line}, column => $self->{column} + 1,
791                        %opt, type => $type);
792      }; # $char_onerror
793    
794      if ($_[3]) {
795        $input = $_[3]->($input);
796        $input->onerror ($char_onerror);
797      } else {
798        $input->onerror ($char_onerror) unless defined $input->onerror;
799      }
800    
801    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
802    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
803    $self->_construct_tree;    $self->_construct_tree;
# Line 393  sub parse_string ($$$;$) { Line 806  sub parse_string ($$$;$) {
806    delete $self->{parse_error}; # remove loop    delete $self->{parse_error}; # remove loop
807    
808    return $self->{document};    return $self->{document};
809  } # parse_string  } # parse_char_stream
810    
811  sub new ($) {  sub new ($) {
812    my $class = shift;    my $class = shift;
813    my $self = bless {}, $class;    my $self = bless {
814    $self->{set_next_char} = sub {      level => {must => 'm',
815      $self->{next_char} = -1;                should => 's',
816                  warn => 'w',
817                  info => 'i',
818                  uncertain => 'u'},
819      }, $class;
820      $self->{set_nc} = sub {
821        $self->{nc} = -1;
822    };    };
823    $self->{parse_error} = sub {    $self->{parse_error} = sub {
824      #      #
# Line 426  sub RCDATA_CONTENT_MODEL () { CM_ENTITY Line 845  sub RCDATA_CONTENT_MODEL () { CM_ENTITY
845  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
846    
847  sub DATA_STATE () { 0 }  sub DATA_STATE () { 0 }
848  sub ENTITY_DATA_STATE () { 1 }  #sub ENTITY_DATA_STATE () { 1 }
849  sub TAG_OPEN_STATE () { 2 }  sub TAG_OPEN_STATE () { 2 }
850  sub CLOSE_TAG_OPEN_STATE () { 3 }  sub CLOSE_TAG_OPEN_STATE () { 3 }
851  sub TAG_NAME_STATE () { 4 }  sub TAG_NAME_STATE () { 4 }
# Line 437  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 Line 856  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8
856  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
857  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
858  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
859  sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }  #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
860  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
861  sub COMMENT_START_STATE () { 14 }  sub COMMENT_START_STATE () { 14 }
862  sub COMMENT_START_DASH_STATE () { 15 }  sub COMMENT_START_DASH_STATE () { 15 }
# Line 460  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STAT Line 879  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STAT
879  sub BOGUS_DOCTYPE_STATE () { 32 }  sub BOGUS_DOCTYPE_STATE () { 32 }
880  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
881  sub SELF_CLOSING_START_TAG_STATE () { 34 }  sub SELF_CLOSING_START_TAG_STATE () { 34 }
882  sub CDATA_BLOCK_STATE () { 35 }  sub CDATA_SECTION_STATE () { 35 }
883    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
884    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
885    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
886    sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
887    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
888    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
889    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
890    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
891    ## NOTE: "Entity data state", "entity in attribute value state", and
892    ## "consume a character reference" algorithm are jointly implemented
893    ## using the following six states:
894    sub ENTITY_STATE () { 44 }
895    sub ENTITY_HASH_STATE () { 45 }
896    sub NCR_NUM_STATE () { 46 }
897    sub HEXREF_X_STATE () { 47 }
898    sub HEXREF_HEX_STATE () { 48 }
899    sub ENTITY_NAME_STATE () { 49 }
900    sub PCDATA_STATE () { 50 } # "data state" in the spec
901    
902  sub DOCTYPE_TOKEN () { 1 }  sub DOCTYPE_TOKEN () { 1 }
903  sub COMMENT_TOKEN () { 2 }  sub COMMENT_TOKEN () { 2 }
# Line 513  sub IN_COLUMN_GROUP_IM () { 0b10 } Line 950  sub IN_COLUMN_GROUP_IM () { 0b10 }
950  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
951    my $self = shift;    my $self = shift;
952    $self->{state} = DATA_STATE; # MUST    $self->{state} = DATA_STATE; # MUST
953      #$self->{s_kwd}; # state keyword - initialized when used
954      #$self->{entity__value}; # initialized when used
955      #$self->{entity__match}; # initialized when used
956    $self->{content_model} = PCDATA_CONTENT_MODEL; # be    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
957    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    undef $self->{ct}; # current token
958    undef $self->{current_attribute};    undef $self->{ca}; # current attribute
959    undef $self->{last_emitted_start_tag_name};    undef $self->{last_stag_name}; # last emitted start tag name
960    undef $self->{last_attribute_value_state};    #$self->{prev_state}; # initialized when used
961    delete $self->{self_closing};    delete $self->{self_closing};
962    $self->{char} = [];    $self->{char_buffer} = '';
963    # $self->{next_char}    $self->{char_buffer_pos} = 0;
964      $self->{nc} = -1; # next input character
965      #$self->{next_nc}
966    !!!next-input-character;    !!!next-input-character;
967    $self->{token} = [];    $self->{token} = [];
968    # $self->{escape}    # $self->{escape}
# Line 531  sub _initialize_tokenizer ($) { Line 973  sub _initialize_tokenizer ($) {
973  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
974  ##   ->{name} (DOCTYPE_TOKEN)  ##   ->{name} (DOCTYPE_TOKEN)
975  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
976  ##   ->{public_identifier} (DOCTYPE_TOKEN)  ##   ->{pubid} (DOCTYPE_TOKEN)
977  ##   ->{system_identifier} (DOCTYPE_TOKEN)  ##   ->{sysid} (DOCTYPE_TOKEN)
978  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
979  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
980  ##        ->{name}  ##        ->{name}
# Line 543  sub _initialize_tokenizer ($) { Line 985  sub _initialize_tokenizer ($) {
985  ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|  ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
986  ##     while the token is pushed back to the stack.  ##     while the token is pushed back to the stack.
987    
 ## ISSUE: "When a DOCTYPE token is created, its  
 ## <i>self-closing flag</i> must be unset (its other state is that it  
 ## be set), and its attributes list must be empty.": Wrong subject?  
   
988  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
989    
990  ## Before each step, UA MAY check to see if either one of the scripts in  ## Before each step, UA MAY check to see if either one of the scripts in
# Line 555  sub _initialize_tokenizer ($) { Line 993  sub _initialize_tokenizer ($) {
993  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
994  ## and removed from the list.  ## and removed from the list.
995    
996  ## NOTE: HTML5 "Writing HTML documents" section, applied to  ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
997  ## documents and not to user agents and conformance checkers,  ## (This requirement was dropped from HTML5 spec, unfortunately.)
998  ## contains some requirements that are not detected by the  
999  ## parsing algorithm:  my $is_space = {
1000  ## - Some requirements on character encoding declarations. ## TODO    0x0009 => 1, # CHARACTER TABULATION (HT)
1001  ## - "Elements MUST NOT contain content that their content model disallows."    0x000A => 1, # LINE FEED (LF)
1002  ##   ... Some are parse error, some are not (will be reported by c.c.).    #0x000B => 0, # LINE TABULATION (VT)
1003  ## - Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) ## TODO    0x000C => 1, # FORM FEED (FF)
1004  ## - Text (in elements, attributes, and comments) SHOULD NOT contain    #0x000D => 1, # CARRIAGE RETURN (CR)
1005  ##   control characters other than space characters. ## TODO: (what is control character? C0, C1 and DEL?  Unicode control character?)    0x0020 => 1, # SPACE (SP)
1006    };
 ## TODO: HTML5 poses authors two SHOULD-level requirements that cannot  
 ## be detected by the HTML5 parsing algorithm:  
 ## - Text,  
1007    
1008  sub _get_next_token ($) {  sub _get_next_token ($) {
1009    my $self = shift;    my $self = shift;
1010    
1011    if ($self->{self_closing}) {    if ($self->{self_closing}) {
1012      !!!parse-error (type => 'nestc', token => $self->{current_token});      !!!parse-error (type => 'nestc', token => $self->{ct});
1013      ## NOTE: The |self_closing| flag is only set by start tag token.      ## NOTE: The |self_closing| flag is only set by start tag token.
1014      ## In addition, when a start tag token is emitted, it is always set to      ## In addition, when a start tag token is emitted, it is always set to
1015      ## |current_token|.      ## |ct|.
1016      delete $self->{self_closing};      delete $self->{self_closing};
1017    }    }
1018    
# Line 587  sub _get_next_token ($) { Line 1022  sub _get_next_token ($) {
1022    }    }
1023    
1024    A: {    A: {
1025      if ($self->{state} == DATA_STATE) {      if ($self->{state} == PCDATA_STATE) {
1026        if ($self->{next_char} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1027    
1028          if ($self->{nc} == 0x0026) { # &
1029            !!!cp (0.1);
1030            ## NOTE: In the spec, the tokenizer is switched to the
1031            ## "entity data state".  In this implementation, the tokenizer
1032            ## is switched to the |ENTITY_STATE|, which is an implementation
1033            ## of the "consume a character reference" algorithm.
1034            $self->{entity_add} = -1;
1035            $self->{prev_state} = DATA_STATE;
1036            $self->{state} = ENTITY_STATE;
1037            !!!next-input-character;
1038            redo A;
1039          } elsif ($self->{nc} == 0x003C) { # <
1040            !!!cp (0.2);
1041            $self->{state} = TAG_OPEN_STATE;
1042            !!!next-input-character;
1043            redo A;
1044          } elsif ($self->{nc} == -1) {
1045            !!!cp (0.3);
1046            !!!emit ({type => END_OF_FILE_TOKEN,
1047                      line => $self->{line}, column => $self->{column}});
1048            last A; ## TODO: ok?
1049          } else {
1050            !!!cp (0.4);
1051            #
1052          }
1053    
1054          # Anything else
1055          my $token = {type => CHARACTER_TOKEN,
1056                       data => chr $self->{nc},
1057                       line => $self->{line}, column => $self->{column},
1058                      };
1059          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1060    
1061          ## Stay in the state.
1062          !!!next-input-character;
1063          !!!emit ($token);
1064          redo A;
1065        } elsif ($self->{state} == DATA_STATE) {
1066          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1067          if ($self->{nc} == 0x0026) { # &
1068            $self->{s_kwd} = '';
1069          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1070              not $self->{escape}) {              not $self->{escape}) {
1071            !!!cp (1);            !!!cp (1);
1072            $self->{state} = ENTITY_DATA_STATE;            ## NOTE: In the spec, the tokenizer is switched to the
1073              ## "entity data state".  In this implementation, the tokenizer
1074              ## is switched to the |ENTITY_STATE|, which is an implementation
1075              ## of the "consume a character reference" algorithm.
1076              $self->{entity_add} = -1;
1077              $self->{prev_state} = DATA_STATE;
1078              $self->{state} = ENTITY_STATE;
1079            !!!next-input-character;            !!!next-input-character;
1080            redo A;            redo A;
1081          } else {          } else {
1082            !!!cp (2);            !!!cp (2);
1083            #            #
1084          }          }
1085        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
1086          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1087            unless ($self->{escape}) {            $self->{s_kwd} .= '-';
1088              if ($self->{prev_char}->[0] == 0x002D and # -            
1089                  $self->{prev_char}->[1] == 0x0021 and # !            if ($self->{s_kwd} eq '<!--') {
1090                  $self->{prev_char}->[2] == 0x003C) { # <              !!!cp (3);
1091                !!!cp (3);              $self->{escape} = 1; # unless $self->{escape};
1092                $self->{escape} = 1;              $self->{s_kwd} = '--';
1093              } else {              #
1094                !!!cp (4);            } elsif ($self->{s_kwd} eq '---') {
1095              }              !!!cp (4);
1096                $self->{s_kwd} = '--';
1097                #
1098            } else {            } else {
1099              !!!cp (5);              !!!cp (5);
1100                #
1101            }            }
1102          }          }
1103                    
1104          #          #
1105        } elsif ($self->{next_char} == 0x003C) { # <        } elsif ($self->{nc} == 0x0021) { # !
1106            if (length $self->{s_kwd}) {
1107              !!!cp (5.1);
1108              $self->{s_kwd} .= '!';
1109              #
1110            } else {
1111              !!!cp (5.2);
1112              #$self->{s_kwd} = '';
1113              #
1114            }
1115            #
1116          } elsif ($self->{nc} == 0x003C) { # <
1117          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1118              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1119               not $self->{escape})) {               not $self->{escape})) {
# Line 626  sub _get_next_token ($) { Line 1123  sub _get_next_token ($) {
1123            redo A;            redo A;
1124          } else {          } else {
1125            !!!cp (7);            !!!cp (7);
1126              $self->{s_kwd} = '';
1127            #            #
1128          }          }
1129        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1130          if ($self->{escape} and          if ($self->{escape} and
1131              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1132            if ($self->{prev_char}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '--') {
               $self->{prev_char}->[1] == 0x002D) { # -  
1133              !!!cp (8);              !!!cp (8);
1134              delete $self->{escape};              delete $self->{escape};
1135            } else {            } else {
# Line 642  sub _get_next_token ($) { Line 1139  sub _get_next_token ($) {
1139            !!!cp (10);            !!!cp (10);
1140          }          }
1141                    
1142            $self->{s_kwd} = '';
1143          #          #
1144        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1145          !!!cp (11);          !!!cp (11);
1146            $self->{s_kwd} = '';
1147          !!!emit ({type => END_OF_FILE_TOKEN,          !!!emit ({type => END_OF_FILE_TOKEN,
1148                    line => $self->{line}, column => $self->{column}});                    line => $self->{line}, column => $self->{column}});
1149          last A; ## TODO: ok?          last A; ## TODO: ok?
1150        } else {        } else {
1151          !!!cp (12);          !!!cp (12);
1152            $self->{s_kwd} = '';
1153            #
1154        }        }
1155    
1156        # Anything else        # Anything else
1157        my $token = {type => CHARACTER_TOKEN,        my $token = {type => CHARACTER_TOKEN,
1158                     data => chr $self->{next_char},                     data => chr $self->{nc},
1159                     line => $self->{line}, column => $self->{column},                     line => $self->{line}, column => $self->{column},
1160                    };                    };
1161        ## Stay in the data state        if ($self->{read_until}->($token->{data}, q[-!<>&],
1162        !!!next-input-character;                                  length $token->{data})) {
1163            $self->{s_kwd} = '';
1164        !!!emit ($token);        }
   
       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  
1165    
1166        unless (defined $token) {        ## Stay in the data state.
1167          if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1168          !!!cp (13);          !!!cp (13);
1169          !!!emit ({type => CHARACTER_TOKEN, data => '&',          $self->{state} = PCDATA_STATE;
                   line => $l, column => $c,  
                  });  
1170        } else {        } else {
1171          !!!cp (14);          !!!cp (14);
1172          !!!emit ($token);          ## Stay in the state.
1173        }        }
1174          !!!next-input-character;
1175          !!!emit ($token);
1176        redo A;        redo A;
1177      } elsif ($self->{state} == TAG_OPEN_STATE) {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1178        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1179          if ($self->{next_char} == 0x002F) { # /          if ($self->{nc} == 0x002F) { # /
1180            !!!cp (15);            !!!cp (15);
1181            !!!next-input-character;            !!!next-input-character;
1182            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1183            redo A;            redo A;
1184            } elsif ($self->{nc} == 0x0021) { # !
1185              !!!cp (15.1);
1186              $self->{s_kwd} = '<' unless $self->{escape};
1187              #
1188          } else {          } else {
1189            !!!cp (16);            !!!cp (16);
1190            ## reconsume            #
           $self->{state} = DATA_STATE;  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev},  
                    });  
   
           redo A;  
1191          }          }
1192    
1193            ## reconsume
1194            $self->{state} = DATA_STATE;
1195            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1196                      line => $self->{line_prev},
1197                      column => $self->{column_prev},
1198                     });
1199            redo A;
1200        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1201          if ($self->{next_char} == 0x0021) { # !          if ($self->{nc} == 0x0021) { # !
1202            !!!cp (17);            !!!cp (17);
1203            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1204            !!!next-input-character;            !!!next-input-character;
1205            redo A;            redo A;
1206          } elsif ($self->{next_char} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1207            !!!cp (18);            !!!cp (18);
1208            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1209            !!!next-input-character;            !!!next-input-character;
1210            redo A;            redo A;
1211          } elsif (0x0041 <= $self->{next_char} and          } elsif (0x0041 <= $self->{nc} and
1212                   $self->{next_char} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1213            !!!cp (19);            !!!cp (19);
1214            $self->{current_token}            $self->{ct}
1215              = {type => START_TAG_TOKEN,              = {type => START_TAG_TOKEN,
1216                 tag_name => chr ($self->{next_char} + 0x0020),                 tag_name => chr ($self->{nc} + 0x0020),
1217                 line => $self->{line_prev},                 line => $self->{line_prev},
1218                 column => $self->{column_prev}};                 column => $self->{column_prev}};
1219            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1220            !!!next-input-character;            !!!next-input-character;
1221            redo A;            redo A;
1222          } elsif (0x0061 <= $self->{next_char} and          } elsif (0x0061 <= $self->{nc} and
1223                   $self->{next_char} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1224            !!!cp (20);            !!!cp (20);
1225            $self->{current_token} = {type => START_TAG_TOKEN,            $self->{ct} = {type => START_TAG_TOKEN,
1226                                      tag_name => chr ($self->{next_char}),                                      tag_name => chr ($self->{nc}),
1227                                      line => $self->{line_prev},                                      line => $self->{line_prev},
1228                                      column => $self->{column_prev}};                                      column => $self->{column_prev}};
1229            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1230            !!!next-input-character;            !!!next-input-character;
1231            redo A;            redo A;
1232          } elsif ($self->{next_char} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1233            !!!cp (21);            !!!cp (21);
1234            !!!parse-error (type => 'empty start tag',            !!!parse-error (type => 'empty start tag',
1235                            line => $self->{line_prev},                            line => $self->{line_prev},
# Line 748  sub _get_next_token ($) { Line 1243  sub _get_next_token ($) {
1243                     });                     });
1244    
1245            redo A;            redo A;
1246          } elsif ($self->{next_char} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1247            !!!cp (22);            !!!cp (22);
1248            !!!parse-error (type => 'pio',            !!!parse-error (type => 'pio',
1249                            line => $self->{line_prev},                            line => $self->{line_prev},
1250                            column => $self->{column_prev});                            column => $self->{column_prev});
1251            $self->{state} = BOGUS_COMMENT_STATE;            $self->{state} = BOGUS_COMMENT_STATE;
1252            $self->{current_token} = {type => COMMENT_TOKEN, data => '',            $self->{ct} = {type => COMMENT_TOKEN, data => '',
1253                                      line => $self->{line_prev},                                      line => $self->{line_prev},
1254                                      column => $self->{column_prev},                                      column => $self->{column_prev},
1255                                     };                                     };
1256            ## $self->{next_char} is intentionally left as is            ## $self->{nc} is intentionally left as is
1257            redo A;            redo A;
1258          } else {          } else {
1259            !!!cp (23);            !!!cp (23);
1260            !!!parse-error (type => 'bare stago');            !!!parse-error (type => 'bare stago',
1261                              line => $self->{line_prev},
1262                              column => $self->{column_prev});
1263            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1264            ## reconsume            ## reconsume
1265    
# Line 777  sub _get_next_token ($) { Line 1274  sub _get_next_token ($) {
1274          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
1275        }        }
1276      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1277          ## NOTE: The "close tag open state" in the spec is implemented as
1278          ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
1279    
1280        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1281        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1282          if (defined $self->{last_emitted_start_tag_name}) {          if (defined $self->{last_stag_name}) {
1283              $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1284            ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>            $self->{s_kwd} = '';
1285            my @next_char;            ## Reconsume.
1286            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...  
           }  
1287          } else {          } else {
1288            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1289              ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1290            !!!cp (28);            !!!cp (28);
           # next-input-character is already done  
1291            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1292              ## Reconsume.
1293            !!!emit ({type => CHARACTER_TOKEN, data => '</',            !!!emit ({type => CHARACTER_TOKEN, data => '</',
1294                      line => $l, column => $c,                      line => $l, column => $c,
1295                     });                     });
1296            redo A;            redo A;
1297          }          }
1298        }        }
1299          
1300        if (0x0041 <= $self->{next_char} and        if (0x0041 <= $self->{nc} and
1301            $self->{next_char} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1302          !!!cp (29);          !!!cp (29);
1303          $self->{current_token}          $self->{ct}
1304              = {type => END_TAG_TOKEN,              = {type => END_TAG_TOKEN,
1305                 tag_name => chr ($self->{next_char} + 0x0020),                 tag_name => chr ($self->{nc} + 0x0020),
1306                 line => $l, column => $c};                 line => $l, column => $c};
1307          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1308          !!!next-input-character;          !!!next-input-character;
1309          redo A;          redo A;
1310        } elsif (0x0061 <= $self->{next_char} and        } elsif (0x0061 <= $self->{nc} and
1311                 $self->{next_char} <= 0x007A) { # a..z                 $self->{nc} <= 0x007A) { # a..z
1312          !!!cp (30);          !!!cp (30);
1313          $self->{current_token} = {type => END_TAG_TOKEN,          $self->{ct} = {type => END_TAG_TOKEN,
1314                                    tag_name => chr ($self->{next_char}),                                    tag_name => chr ($self->{nc}),
1315                                    line => $l, column => $c};                                    line => $l, column => $c};
1316          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1317          !!!next-input-character;          !!!next-input-character;
1318          redo A;          redo A;
1319        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1320          !!!cp (31);          !!!cp (31);
1321          !!!parse-error (type => 'empty end tag',          !!!parse-error (type => 'empty end tag',
1322                          line => $self->{line_prev}, ## "<" in "</>"                          line => $self->{line_prev}, ## "<" in "</>"
# Line 867  sub _get_next_token ($) { Line 1324  sub _get_next_token ($) {
1324          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1325          !!!next-input-character;          !!!next-input-character;
1326          redo A;          redo A;
1327        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1328          !!!cp (32);          !!!cp (32);
1329          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1330          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
# Line 882  sub _get_next_token ($) { Line 1339  sub _get_next_token ($) {
1339          !!!cp (33);          !!!cp (33);
1340          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1341          $self->{state} = BOGUS_COMMENT_STATE;          $self->{state} = BOGUS_COMMENT_STATE;
1342          $self->{current_token} = {type => COMMENT_TOKEN, data => '',          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1343                                    line => $self->{line_prev}, # "<" of "</"                                    line => $self->{line_prev}, # "<" of "</"
1344                                    column => $self->{column_prev} - 1,                                    column => $self->{column_prev} - 1,
1345                                   };                                   };
1346          ## $self->{next_char} is intentionally left as is          ## NOTE: $self->{nc} is intentionally left as is.
1347          redo A;          ## Although the "anything else" case of the spec not explicitly
1348            ## states that the next input character is to be reconsumed,
1349            ## it will be included to the |data| of the comment token
1350            ## generated from the bogus end tag, as defined in the
1351            ## "bogus comment state" entry.
1352            redo A;
1353          }
1354        } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1355          my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1356          if (length $ch) {
1357            my $CH = $ch;
1358            $ch =~ tr/a-z/A-Z/;
1359            my $nch = chr $self->{nc};
1360            if ($nch eq $ch or $nch eq $CH) {
1361              !!!cp (24);
1362              ## Stay in the state.
1363              $self->{s_kwd} .= $nch;
1364              !!!next-input-character;
1365              redo A;
1366            } else {
1367              !!!cp (25);
1368              $self->{state} = DATA_STATE;
1369              ## Reconsume.
1370              !!!emit ({type => CHARACTER_TOKEN,
1371                        data => '</' . $self->{s_kwd},
1372                        line => $self->{line_prev},
1373                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1374                       });
1375              redo A;
1376            }
1377          } else { # after "<{tag-name}"
1378            unless ($is_space->{$self->{nc}} or
1379                    {
1380                     0x003E => 1, # >
1381                     0x002F => 1, # /
1382                     -1 => 1, # EOF
1383                    }->{$self->{nc}}) {
1384              !!!cp (26);
1385              ## Reconsume.
1386              $self->{state} = DATA_STATE;
1387              !!!emit ({type => CHARACTER_TOKEN,
1388                        data => '</' . $self->{s_kwd},
1389                        line => $self->{line_prev},
1390                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1391                       });
1392              redo A;
1393            } else {
1394              !!!cp (27);
1395              $self->{ct}
1396                  = {type => END_TAG_TOKEN,
1397                     tag_name => $self->{last_stag_name},
1398                     line => $self->{line_prev},
1399                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1400              $self->{state} = TAG_NAME_STATE;
1401              ## Reconsume.
1402              redo A;
1403            }
1404        }        }
1405      } elsif ($self->{state} == TAG_NAME_STATE) {      } elsif ($self->{state} == TAG_NAME_STATE) {
1406        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  
1407          !!!cp (34);          !!!cp (34);
1408          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1409          !!!next-input-character;          !!!next-input-character;
1410          redo A;          redo A;
1411        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1412          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1413            !!!cp (35);            !!!cp (35);
1414            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1415          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1416            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1417            #if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1418            #  ## NOTE: This should never be reached.            #  ## NOTE: This should never be reached.
1419            #  !!! cp (36);            #  !!! cp (36);
1420            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 913  sub _get_next_token ($) { Line 1422  sub _get_next_token ($) {
1422              !!!cp (37);              !!!cp (37);
1423            #}            #}
1424          } else {          } else {
1425            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1426          }          }
1427          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1428          !!!next-input-character;          !!!next-input-character;
1429    
1430          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1431    
1432          redo A;          redo A;
1433        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1434                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1435          !!!cp (38);          !!!cp (38);
1436          $self->{current_token}->{tag_name} .= chr ($self->{next_char} + 0x0020);          $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1437            # start tag or end tag            # start tag or end tag
1438          ## Stay in this state          ## Stay in this state
1439          !!!next-input-character;          !!!next-input-character;
1440          redo A;          redo A;
1441        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1442          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1443          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1444            !!!cp (39);            !!!cp (39);
1445            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1446          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1447            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1448            #if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1449            #  ## NOTE: This state should never be reached.            #  ## NOTE: This state should never be reached.
1450            #  !!! cp (40);            #  !!! cp (40);
1451            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 944  sub _get_next_token ($) { Line 1453  sub _get_next_token ($) {
1453              !!!cp (41);              !!!cp (41);
1454            #}            #}
1455          } else {          } else {
1456            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1457          }          }
1458          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1459          # reconsume          # reconsume
1460    
1461          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1462    
1463          redo A;          redo A;
1464        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1465          !!!cp (42);          !!!cp (42);
1466          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
1467          !!!next-input-character;          !!!next-input-character;
1468          redo A;          redo A;
1469        } else {        } else {
1470          !!!cp (44);          !!!cp (44);
1471          $self->{current_token}->{tag_name} .= chr $self->{next_char};          $self->{ct}->{tag_name} .= chr $self->{nc};
1472            # start tag or end tag            # start tag or end tag
1473          ## Stay in the state          ## Stay in the state
1474          !!!next-input-character;          !!!next-input-character;
1475          redo A;          redo A;
1476        }        }
1477      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1478        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  
1479          !!!cp (45);          !!!cp (45);
1480          ## Stay in the state          ## Stay in the state
1481          !!!next-input-character;          !!!next-input-character;
1482          redo A;          redo A;
1483        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1484          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1485            !!!cp (46);            !!!cp (46);
1486            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1487          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1488            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1489            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1490              !!!cp (47);              !!!cp (47);
1491              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1492            } else {            } else {
1493              !!!cp (48);              !!!cp (48);
1494            }            }
1495          } else {          } else {
1496            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1497          }          }
1498          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1499          !!!next-input-character;          !!!next-input-character;
1500    
1501          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1502    
1503          redo A;          redo A;
1504        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1505                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1506          !!!cp (49);          !!!cp (49);
1507          $self->{current_attribute}          $self->{ca}
1508              = {name => chr ($self->{next_char} + 0x0020),              = {name => chr ($self->{nc} + 0x0020),
1509                 value => '',                 value => '',
1510                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1511          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1512          !!!next-input-character;          !!!next-input-character;
1513          redo A;          redo A;
1514        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1515          !!!cp (50);          !!!cp (50);
1516          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
1517          !!!next-input-character;          !!!next-input-character;
1518          redo A;          redo A;
1519        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1520          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1521          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1522            !!!cp (52);            !!!cp (52);
1523            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1524          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1525            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1526            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1527              !!!cp (53);              !!!cp (53);
1528              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1529            } else {            } else {
1530              !!!cp (54);              !!!cp (54);
1531            }            }
1532          } else {          } else {
1533            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1534          }          }
1535          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1536          # reconsume          # reconsume
1537    
1538          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1539    
1540          redo A;          redo A;
1541        } else {        } else {
# Line 1038  sub _get_next_token ($) { Line 1543  sub _get_next_token ($) {
1543               0x0022 => 1, # "               0x0022 => 1, # "
1544               0x0027 => 1, # '               0x0027 => 1, # '
1545               0x003D => 1, # =               0x003D => 1, # =
1546              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1547            !!!cp (55);            !!!cp (55);
1548            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1549          } else {          } else {
1550            !!!cp (56);            !!!cp (56);
1551          }          }
1552          $self->{current_attribute}          $self->{ca}
1553              = {name => chr ($self->{next_char}),              = {name => chr ($self->{nc}),
1554                 value => '',                 value => '',
1555                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1556          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
# Line 1054  sub _get_next_token ($) { Line 1559  sub _get_next_token ($) {
1559        }        }
1560      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1561        my $before_leave = sub {        my $before_leave = sub {
1562          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1563              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1564            !!!cp (57);            !!!cp (57);
1565            !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name}, line => $self->{current_attribute}->{line}, column => $self->{current_attribute}->{column});            !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1566            ## Discard $self->{current_attribute} # MUST            ## Discard $self->{ca} # MUST
1567          } else {          } else {
1568            !!!cp (58);            !!!cp (58);
1569            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            $self->{ct}->{attributes}->{$self->{ca}->{name}}
1570              = $self->{current_attribute};              = $self->{ca};
1571          }          }
1572        }; # $before_leave        }; # $before_leave
1573    
1574        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  
1575          !!!cp (59);          !!!cp (59);
1576          $before_leave->();          $before_leave->();
1577          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1578          !!!next-input-character;          !!!next-input-character;
1579          redo A;          redo A;
1580        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1581          !!!cp (60);          !!!cp (60);
1582          $before_leave->();          $before_leave->();
1583          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1584          !!!next-input-character;          !!!next-input-character;
1585          redo A;          redo A;
1586        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1587          $before_leave->();          $before_leave->();
1588          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1589            !!!cp (61);            !!!cp (61);
1590            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1591          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1592            !!!cp (62);            !!!cp (62);
1593            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1594            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1595              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1596            }            }
1597          } else {          } else {
1598            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1599          }          }
1600          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1601          !!!next-input-character;          !!!next-input-character;
1602    
1603          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1604    
1605          redo A;          redo A;
1606        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1607                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1608          !!!cp (63);          !!!cp (63);
1609          $self->{current_attribute}->{name} .= chr ($self->{next_char} + 0x0020);          $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1610          ## Stay in the state          ## Stay in the state
1611          !!!next-input-character;          !!!next-input-character;
1612          redo A;          redo A;
1613        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1614          !!!cp (64);          !!!cp (64);
1615          $before_leave->();          $before_leave->();
1616          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
1617          !!!next-input-character;          !!!next-input-character;
1618          redo A;          redo A;
1619        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1620          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1621          $before_leave->();          $before_leave->();
1622          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1623            !!!cp (66);            !!!cp (66);
1624            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1625          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1626            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1627            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1628              !!!cp (67);              !!!cp (67);
1629              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1630            } else {            } else {
# Line 1131  sub _get_next_token ($) { Line 1632  sub _get_next_token ($) {
1632              !!!cp (68);              !!!cp (68);
1633            }            }
1634          } else {          } else {
1635            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1636          }          }
1637          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1638          # reconsume          # reconsume
1639    
1640          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1641    
1642          redo A;          redo A;
1643        } else {        } else {
1644          if ($self->{next_char} == 0x0022 or # "          if ($self->{nc} == 0x0022 or # "
1645              $self->{next_char} == 0x0027) { # '              $self->{nc} == 0x0027) { # '
1646            !!!cp (69);            !!!cp (69);
1647            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1648          } else {          } else {
1649            !!!cp (70);            !!!cp (70);
1650          }          }
1651          $self->{current_attribute}->{name} .= chr ($self->{next_char});          $self->{ca}->{name} .= chr ($self->{nc});
1652          ## Stay in the state          ## Stay in the state
1653          !!!next-input-character;          !!!next-input-character;
1654          redo A;          redo A;
1655        }        }
1656      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1657        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  
1658          !!!cp (71);          !!!cp (71);
1659          ## Stay in the state          ## Stay in the state
1660          !!!next-input-character;          !!!next-input-character;
1661          redo A;          redo A;
1662        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1663          !!!cp (72);          !!!cp (72);
1664          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1665          !!!next-input-character;          !!!next-input-character;
1666          redo A;          redo A;
1667        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1668          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1669            !!!cp (73);            !!!cp (73);
1670            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1671          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1672            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1673            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1674              !!!cp (74);              !!!cp (74);
1675              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1676            } else {            } else {
# Line 1181  sub _get_next_token ($) { Line 1678  sub _get_next_token ($) {
1678              !!!cp (75);              !!!cp (75);
1679            }            }
1680          } else {          } else {
1681            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1682          }          }
1683          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1684          !!!next-input-character;          !!!next-input-character;
1685    
1686          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1687    
1688          redo A;          redo A;
1689        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1690                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1691          !!!cp (76);          !!!cp (76);
1692          $self->{current_attribute}          $self->{ca}
1693              = {name => chr ($self->{next_char} + 0x0020),              = {name => chr ($self->{nc} + 0x0020),
1694                 value => '',                 value => '',
1695                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1696          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1697          !!!next-input-character;          !!!next-input-character;
1698          redo A;          redo A;
1699        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1700          !!!cp (77);          !!!cp (77);
1701          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
1702          !!!next-input-character;          !!!next-input-character;
1703          redo A;          redo A;
1704        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1705          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1706          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1707            !!!cp (79);            !!!cp (79);
1708            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1709          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1710            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1711            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1712              !!!cp (80);              !!!cp (80);
1713              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1714            } else {            } else {
# Line 1219  sub _get_next_token ($) { Line 1716  sub _get_next_token ($) {
1716              !!!cp (81);              !!!cp (81);
1717            }            }
1718          } else {          } else {
1719            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1720          }          }
1721          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1722          # reconsume          # reconsume
1723    
1724          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1725    
1726          redo A;          redo A;
1727        } else {        } else {
1728          !!!cp (82);          if ($self->{nc} == 0x0022 or # "
1729          $self->{current_attribute}              $self->{nc} == 0x0027) { # '
1730              = {name => chr ($self->{next_char}),            !!!cp (78);
1731              !!!parse-error (type => 'bad attribute name');
1732            } else {
1733              !!!cp (82);
1734            }
1735            $self->{ca}
1736                = {name => chr ($self->{nc}),
1737                 value => '',                 value => '',
1738                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1739          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
# Line 1238  sub _get_next_token ($) { Line 1741  sub _get_next_token ($) {
1741          redo A;                  redo A;        
1742        }        }
1743      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1744        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        
1745          !!!cp (83);          !!!cp (83);
1746          ## Stay in the state          ## Stay in the state
1747          !!!next-input-character;          !!!next-input-character;
1748          redo A;          redo A;
1749        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1750          !!!cp (84);          !!!cp (84);
1751          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1752          !!!next-input-character;          !!!next-input-character;
1753          redo A;          redo A;
1754        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1755          !!!cp (85);          !!!cp (85);
1756          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1757          ## reconsume          ## reconsume
1758          redo A;          redo A;
1759        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1760          !!!cp (86);          !!!cp (86);
1761          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1762          !!!next-input-character;          !!!next-input-character;
1763          redo A;          redo A;
1764        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1765          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          !!!parse-error (type => 'empty unquoted attribute value');
1766            if ($self->{ct}->{type} == START_TAG_TOKEN) {
1767            !!!cp (87);            !!!cp (87);
1768            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1769          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1770            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1771            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1772              !!!cp (88);              !!!cp (88);
1773              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1774            } else {            } else {
# Line 1276  sub _get_next_token ($) { Line 1776  sub _get_next_token ($) {
1776              !!!cp (89);              !!!cp (89);
1777            }            }
1778          } else {          } else {
1779            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1780          }          }
1781          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1782          !!!next-input-character;          !!!next-input-character;
1783    
1784          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1785    
1786          redo A;          redo A;
1787        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1788          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1789          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1790            !!!cp (90);            !!!cp (90);
1791            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1792          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1793            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1794            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1795              !!!cp (91);              !!!cp (91);
1796              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1797            } else {            } else {
# Line 1299  sub _get_next_token ($) { Line 1799  sub _get_next_token ($) {
1799              !!!cp (92);              !!!cp (92);
1800            }            }
1801          } else {          } else {
1802            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1803          }          }
1804          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1805          ## reconsume          ## reconsume
1806    
1807          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1808    
1809          redo A;          redo A;
1810        } else {        } else {
1811          if ($self->{next_char} == 0x003D) { # =          if ($self->{nc} == 0x003D) { # =
1812            !!!cp (93);            !!!cp (93);
1813            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1814          } else {          } else {
1815            !!!cp (94);            !!!cp (94);
1816          }          }
1817          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1818          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1819          !!!next-input-character;          !!!next-input-character;
1820          redo A;          redo A;
1821        }        }
1822      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1823        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1824          !!!cp (95);          !!!cp (95);
1825          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1826          !!!next-input-character;          !!!next-input-character;
1827          redo A;          redo A;
1828        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1829          !!!cp (96);          !!!cp (96);
1830          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1831          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1832            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1833            ## implementation of the "consume a character reference" algorithm.
1834            $self->{prev_state} = $self->{state};
1835            $self->{entity_add} = 0x0022; # "
1836            $self->{state} = ENTITY_STATE;
1837          !!!next-input-character;          !!!next-input-character;
1838          redo A;          redo A;
1839        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1840          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1841          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1842            !!!cp (97);            !!!cp (97);
1843            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1844          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1845            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1846            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1847              !!!cp (98);              !!!cp (98);
1848              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1849            } else {            } else {
# Line 1346  sub _get_next_token ($) { Line 1851  sub _get_next_token ($) {
1851              !!!cp (99);              !!!cp (99);
1852            }            }
1853          } else {          } else {
1854            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1855          }          }
1856          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1857          ## reconsume          ## reconsume
1858    
1859          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1860    
1861          redo A;          redo A;
1862        } else {        } else {
1863          !!!cp (100);          !!!cp (100);
1864          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1865            $self->{read_until}->($self->{ca}->{value},
1866                                  q["&],
1867                                  length $self->{ca}->{value});
1868    
1869          ## Stay in the state          ## Stay in the state
1870          !!!next-input-character;          !!!next-input-character;
1871          redo A;          redo A;
1872        }        }
1873      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1874        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1875          !!!cp (101);          !!!cp (101);
1876          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1877          !!!next-input-character;          !!!next-input-character;
1878          redo A;          redo A;
1879        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1880          !!!cp (102);          !!!cp (102);
1881          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1882          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1883            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1884            ## implementation of the "consume a character reference" algorithm.
1885            $self->{entity_add} = 0x0027; # '
1886            $self->{prev_state} = $self->{state};
1887            $self->{state} = ENTITY_STATE;
1888          !!!next-input-character;          !!!next-input-character;
1889          redo A;          redo A;
1890        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1891          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1892          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1893            !!!cp (103);            !!!cp (103);
1894            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1895          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1896            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1897            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1898              !!!cp (104);              !!!cp (104);
1899              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1900            } else {            } else {
# Line 1388  sub _get_next_token ($) { Line 1902  sub _get_next_token ($) {
1902              !!!cp (105);              !!!cp (105);
1903            }            }
1904          } else {          } else {
1905            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1906          }          }
1907          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1908          ## reconsume          ## reconsume
1909    
1910          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1911    
1912          redo A;          redo A;
1913        } else {        } else {
1914          !!!cp (106);          !!!cp (106);
1915          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1916            $self->{read_until}->($self->{ca}->{value},
1917                                  q['&],
1918                                  length $self->{ca}->{value});
1919    
1920          ## Stay in the state          ## Stay in the state
1921          !!!next-input-character;          !!!next-input-character;
1922          redo A;          redo A;
1923        }        }
1924      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1925        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  
1926          !!!cp (107);          !!!cp (107);
1927          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1928          !!!next-input-character;          !!!next-input-character;
1929          redo A;          redo A;
1930        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1931          !!!cp (108);          !!!cp (108);
1932          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1933          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1934            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1935            ## implementation of the "consume a character reference" algorithm.
1936            $self->{entity_add} = -1;
1937            $self->{prev_state} = $self->{state};
1938            $self->{state} = ENTITY_STATE;
1939          !!!next-input-character;          !!!next-input-character;
1940          redo A;          redo A;
1941        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1942          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1943            !!!cp (109);            !!!cp (109);
1944            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1945          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1946            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1947            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1948              !!!cp (110);              !!!cp (110);
1949              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1950            } else {            } else {
# Line 1433  sub _get_next_token ($) { Line 1952  sub _get_next_token ($) {
1952              !!!cp (111);              !!!cp (111);
1953            }            }
1954          } else {          } else {
1955            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1956          }          }
1957          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1958          !!!next-input-character;          !!!next-input-character;
1959    
1960          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1961    
1962          redo A;          redo A;
1963        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1964          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1965          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1966            !!!cp (112);            !!!cp (112);
1967            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1968          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1969            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1970            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1971              !!!cp (113);              !!!cp (113);
1972              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1973            } else {            } else {
# Line 1456  sub _get_next_token ($) { Line 1975  sub _get_next_token ($) {
1975              !!!cp (114);              !!!cp (114);
1976            }            }
1977          } else {          } else {
1978            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1979          }          }
1980          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1981          ## reconsume          ## reconsume
1982    
1983          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1984    
1985          redo A;          redo A;
1986        } else {        } else {
# Line 1469  sub _get_next_token ($) { Line 1988  sub _get_next_token ($) {
1988               0x0022 => 1, # "               0x0022 => 1, # "
1989               0x0027 => 1, # '               0x0027 => 1, # '
1990               0x003D => 1, # =               0x003D => 1, # =
1991              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1992            !!!cp (115);            !!!cp (115);
1993            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1994          } else {          } else {
1995            !!!cp (116);            !!!cp (116);
1996          }          }
1997          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1998            $self->{read_until}->($self->{ca}->{value},
1999                                  q["'=& >],
2000                                  length $self->{ca}->{value});
2001    
2002          ## Stay in the state          ## Stay in the state
2003          !!!next-input-character;          !!!next-input-character;
2004          redo A;          redo A;
2005        }        }
     } 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;  
2006      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2007        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  
2008          !!!cp (118);          !!!cp (118);
2009          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2010          !!!next-input-character;          !!!next-input-character;
2011          redo A;          redo A;
2012        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2013          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2014            !!!cp (119);            !!!cp (119);
2015            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
2016          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2017            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2018            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2019              !!!cp (120);              !!!cp (120);
2020              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2021            } else {            } else {
# Line 1526  sub _get_next_token ($) { Line 2023  sub _get_next_token ($) {
2023              !!!cp (121);              !!!cp (121);
2024            }            }
2025          } else {          } else {
2026            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
2027          }          }
2028          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2029          !!!next-input-character;          !!!next-input-character;
2030    
2031          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2032    
2033          redo A;          redo A;
2034        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
2035          !!!cp (122);          !!!cp (122);
2036          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
2037          !!!next-input-character;          !!!next-input-character;
2038          redo A;          redo A;
2039          } elsif ($self->{nc} == -1) {
2040            !!!parse-error (type => 'unclosed tag');
2041            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2042              !!!cp (122.3);
2043              $self->{last_stag_name} = $self->{ct}->{tag_name};
2044            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2045              if ($self->{ct}->{attributes}) {
2046                !!!cp (122.1);
2047                !!!parse-error (type => 'end tag attribute');
2048              } else {
2049                ## NOTE: This state should never be reached.
2050                !!!cp (122.2);
2051              }
2052            } else {
2053              die "$0: $self->{ct}->{type}: Unknown token type";
2054            }
2055            $self->{state} = DATA_STATE;
2056            ## Reconsume.
2057            !!!emit ($self->{ct}); # start tag or end tag
2058            redo A;
2059        } else {        } else {
2060          !!!cp ('124.1');          !!!cp ('124.1');
2061          !!!parse-error (type => 'no space between attributes');          !!!parse-error (type => 'no space between attributes');
# Line 1547  sub _get_next_token ($) { Line 2064  sub _get_next_token ($) {
2064          redo A;          redo A;
2065        }        }
2066      } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {      } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2067        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2068          if ($self->{current_token}->{type} == END_TAG_TOKEN) {          if ($self->{ct}->{type} == END_TAG_TOKEN) {
2069            !!!cp ('124.2');            !!!cp ('124.2');
2070            !!!parse-error (type => 'nestc', token => $self->{current_token});            !!!parse-error (type => 'nestc', token => $self->{ct});
2071            ## TODO: Different type than slash in start tag            ## TODO: Different type than slash in start tag
2072            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2073            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2074              !!!cp ('124.4');              !!!cp ('124.4');
2075              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2076            } else {            } else {
# Line 1568  sub _get_next_token ($) { Line 2085  sub _get_next_token ($) {
2085          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2086          !!!next-input-character;          !!!next-input-character;
2087    
2088          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2089    
2090          redo A;          redo A;
2091          } elsif ($self->{nc} == -1) {
2092            !!!parse-error (type => 'unclosed tag');
2093            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2094              !!!cp (124.7);
2095              $self->{last_stag_name} = $self->{ct}->{tag_name};
2096            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2097              if ($self->{ct}->{attributes}) {
2098                !!!cp (124.5);
2099                !!!parse-error (type => 'end tag attribute');
2100              } else {
2101                ## NOTE: This state should never be reached.
2102                !!!cp (124.6);
2103              }
2104            } else {
2105              die "$0: $self->{ct}->{type}: Unknown token type";
2106            }
2107            $self->{state} = DATA_STATE;
2108            ## Reconsume.
2109            !!!emit ($self->{ct}); # start tag or end tag
2110            redo A;
2111        } else {        } else {
2112          !!!cp ('124.4');          !!!cp ('124.4');
2113          !!!parse-error (type => 'nestc');          !!!parse-error (type => 'nestc');
# Line 1581  sub _get_next_token ($) { Line 2118  sub _get_next_token ($) {
2118        }        }
2119      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2120        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
         
       ## NOTE: Set by the previous state  
       #my $token = {type => COMMENT_TOKEN, data => ''};  
   
       BC: {  
         if ($self->{next_char} == 0x003E) { # >  
           !!!cp (124);  
           $self->{state} = DATA_STATE;  
           !!!next-input-character;  
2121    
2122            !!!emit ($self->{current_token}); # comment        ## NOTE: Unlike spec's "bogus comment state", this implementation
2123          ## consumes characters one-by-one basis.
2124            redo A;        
2125          } elsif ($self->{next_char} == -1) {        if ($self->{nc} == 0x003E) { # >
2126            !!!cp (125);          !!!cp (124);
2127            $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2128            ## reconsume          !!!next-input-character;
2129    
2130            !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2131            redo A;
2132          } elsif ($self->{nc} == -1) {
2133            !!!cp (125);
2134            $self->{state} = DATA_STATE;
2135            ## reconsume
2136    
2137            redo A;          !!!emit ($self->{ct}); # comment
2138          } else {          redo A;
2139            !!!cp (126);        } else {
2140            $self->{current_token}->{data} .= chr ($self->{next_char}); # comment          !!!cp (126);
2141            !!!next-input-character;          $self->{ct}->{data} .= chr ($self->{nc}); # comment
2142            redo BC;          $self->{read_until}->($self->{ct}->{data},
2143          }                                q[>],
2144        } # BC                                length $self->{ct}->{data});
2145    
2146        die "$0: _get_next_token: unexpected case [BC]";          ## Stay in the state.
2147            !!!next-input-character;
2148            redo A;
2149          }
2150      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2151        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
   
       my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1);  
   
       my @next_char;  
       push @next_char, $self->{next_char};  
2152                
2153        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2154            !!!cp (133);
2155            $self->{state} = MD_HYPHEN_STATE;
2156          !!!next-input-character;          !!!next-input-character;
2157          push @next_char, $self->{next_char};          redo A;
2158          if ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x0044 or # D
2159            !!!cp (127);                 $self->{nc} == 0x0064) { # d
2160            $self->{current_token} = {type => COMMENT_TOKEN, data => '',          ## ASCII case-insensitive.
2161                                      line => $l, column => $c,          !!!cp (130);
2162                                     };          $self->{state} = MD_DOCTYPE_STATE;
2163            $self->{state} = COMMENT_START_STATE;          $self->{s_kwd} = chr $self->{nc};
           !!!next-input-character;  
           redo A;  
         } else {  
           !!!cp (128);  
         }  
       } elsif ($self->{next_char} == 0x0044 or # D  
                $self->{next_char} == 0x0064) { # d  
2164          !!!next-input-character;          !!!next-input-character;
2165          push @next_char, $self->{next_char};          redo A;
         if ($self->{next_char} == 0x004F or # O  
             $self->{next_char} == 0x006F) { # o  
           !!!next-input-character;  
           push @next_char, $self->{next_char};  
           if ($self->{next_char} == 0x0043 or # C  
               $self->{next_char} == 0x0063) { # c  
             !!!next-input-character;  
             push @next_char, $self->{next_char};  
             if ($self->{next_char} == 0x0054 or # T  
                 $self->{next_char} == 0x0074) { # t  
               !!!next-input-character;  
               push @next_char, $self->{next_char};  
               if ($self->{next_char} == 0x0059 or # Y  
                   $self->{next_char} == 0x0079) { # y  
                 !!!next-input-character;  
                 push @next_char, $self->{next_char};  
                 if ($self->{next_char} == 0x0050 or # P  
                     $self->{next_char} == 0x0070) { # p  
                   !!!next-input-character;  
                   push @next_char, $self->{next_char};  
                   if ($self->{next_char} == 0x0045 or # E  
                       $self->{next_char} == 0x0065) { # e  
                     !!!cp (129);  
                     ## TODO: What a stupid code this is!  
                     $self->{state} = DOCTYPE_STATE;  
                     $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);  
         }  
2166        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2167                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2168                 $self->{next_char} == 0x005B) { # [                 $self->{nc} == 0x005B) { # [
2169            !!!cp (135.4);                
2170            $self->{state} = MD_CDATA_STATE;
2171            $self->{s_kwd} = '[';
2172          !!!next-input-character;          !!!next-input-character;
2173          push @next_char, $self->{next_char};          redo A;
         if ($self->{next_char} == 0x0043) { # C  
           !!!next-input-character;  
           push @next_char, $self->{next_char};  
           if ($self->{next_char} == 0x0044) { # D  
             !!!next-input-character;  
             push @next_char, $self->{next_char};  
             if ($self->{next_char} == 0x0041) { # A  
               !!!next-input-character;  
               push @next_char, $self->{next_char};  
               if ($self->{next_char} == 0x0054) { # T  
                 !!!next-input-character;  
                 push @next_char, $self->{next_char};  
                 if ($self->{next_char} == 0x0041) { # A  
                   !!!next-input-character;  
                   push @next_char, $self->{next_char};  
                   if ($self->{next_char} == 0x005B) { # [  
                     !!!cp (135.1);  
                     $self->{state} = CDATA_BLOCK_STATE;  
                     !!!next-input-character;  
                     redo A;  
                   } else {  
                     !!!cp (135.2);  
                   }  
                 } else {  
                   !!!cp (135.3);  
                 }  
               } else {  
                 !!!cp (135.4);                  
               }  
             } else {  
               !!!cp (135.5);  
             }  
           } else {  
             !!!cp (135.6);  
           }  
         } else {  
           !!!cp (135.7);  
         }  
2174        } else {        } else {
2175          !!!cp (136);          !!!cp (136);
2176        }        }
2177    
2178        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment',
2179        $self->{next_char} = shift @next_char;                        line => $self->{line_prev},
2180        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2181          ## Reconsume.
2182        $self->{state} = BOGUS_COMMENT_STATE;        $self->{state} = BOGUS_COMMENT_STATE;
2183        $self->{current_token} = {type => COMMENT_TOKEN, data => '',        $self->{ct} = {type => COMMENT_TOKEN, data => '',
2184                                  line => $l, column => $c,                                  line => $self->{line_prev},
2185                                    column => $self->{column_prev} - 1,
2186                                 };                                 };
2187        redo A;        redo A;
2188              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2189        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2190        ## 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);
2191            $self->{ct} = {type => COMMENT_TOKEN, data => '',
2192                                      line => $self->{line_prev},
2193                                      column => $self->{column_prev} - 2,
2194                                     };
2195            $self->{state} = COMMENT_START_STATE;
2196            !!!next-input-character;
2197            redo A;
2198          } else {
2199            !!!cp (128);
2200            !!!parse-error (type => 'bogus comment',
2201                            line => $self->{line_prev},
2202                            column => $self->{column_prev} - 2);
2203            $self->{state} = BOGUS_COMMENT_STATE;
2204            ## Reconsume.
2205            $self->{ct} = {type => COMMENT_TOKEN,
2206                                      data => '-',
2207                                      line => $self->{line_prev},
2208                                      column => $self->{column_prev} - 2,
2209                                     };
2210            redo A;
2211          }
2212        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2213          ## ASCII case-insensitive.
2214          if ($self->{nc} == [
2215                undef,
2216                0x004F, # O
2217                0x0043, # C
2218                0x0054, # T
2219                0x0059, # Y
2220                0x0050, # P
2221              ]->[length $self->{s_kwd}] or
2222              $self->{nc} == [
2223                undef,
2224                0x006F, # o
2225                0x0063, # c
2226                0x0074, # t
2227                0x0079, # y
2228                0x0070, # p
2229              ]->[length $self->{s_kwd}]) {
2230            !!!cp (131);
2231            ## Stay in the state.
2232            $self->{s_kwd} .= chr $self->{nc};
2233            !!!next-input-character;
2234            redo A;
2235          } elsif ((length $self->{s_kwd}) == 6 and
2236                   ($self->{nc} == 0x0045 or # E
2237                    $self->{nc} == 0x0065)) { # e
2238            !!!cp (129);
2239            $self->{state} = DOCTYPE_STATE;
2240            $self->{ct} = {type => DOCTYPE_TOKEN,
2241                                      quirks => 1,
2242                                      line => $self->{line_prev},
2243                                      column => $self->{column_prev} - 7,
2244                                     };
2245            !!!next-input-character;
2246            redo A;
2247          } else {
2248            !!!cp (132);        
2249            !!!parse-error (type => 'bogus comment',
2250                            line => $self->{line_prev},
2251                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2252            $self->{state} = BOGUS_COMMENT_STATE;
2253            ## Reconsume.
2254            $self->{ct} = {type => COMMENT_TOKEN,
2255                                      data => $self->{s_kwd},
2256                                      line => $self->{line_prev},
2257                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2258                                     };
2259            redo A;
2260          }
2261        } elsif ($self->{state} == MD_CDATA_STATE) {
2262          if ($self->{nc} == {
2263                '[' => 0x0043, # C
2264                '[C' => 0x0044, # D
2265                '[CD' => 0x0041, # A
2266                '[CDA' => 0x0054, # T
2267                '[CDAT' => 0x0041, # A
2268              }->{$self->{s_kwd}}) {
2269            !!!cp (135.1);
2270            ## Stay in the state.
2271            $self->{s_kwd} .= chr $self->{nc};
2272            !!!next-input-character;
2273            redo A;
2274          } elsif ($self->{s_kwd} eq '[CDATA' and
2275                   $self->{nc} == 0x005B) { # [
2276            !!!cp (135.2);
2277            $self->{ct} = {type => CHARACTER_TOKEN,
2278                                      data => '',
2279                                      line => $self->{line_prev},
2280                                      column => $self->{column_prev} - 7};
2281            $self->{state} = CDATA_SECTION_STATE;
2282            !!!next-input-character;
2283            redo A;
2284          } else {
2285            !!!cp (135.3);
2286            !!!parse-error (type => 'bogus comment',
2287                            line => $self->{line_prev},
2288                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2289            $self->{state} = BOGUS_COMMENT_STATE;
2290            ## Reconsume.
2291            $self->{ct} = {type => COMMENT_TOKEN,
2292                                      data => $self->{s_kwd},
2293                                      line => $self->{line_prev},
2294                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2295                                     };
2296            redo A;
2297          }
2298      } elsif ($self->{state} == COMMENT_START_STATE) {      } elsif ($self->{state} == COMMENT_START_STATE) {
2299        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2300          !!!cp (137);          !!!cp (137);
2301          $self->{state} = COMMENT_START_DASH_STATE;          $self->{state} = COMMENT_START_DASH_STATE;
2302          !!!next-input-character;          !!!next-input-character;
2303          redo A;          redo A;
2304        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2305          !!!cp (138);          !!!cp (138);
2306          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2307          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2308          !!!next-input-character;          !!!next-input-character;
2309    
2310          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2311    
2312          redo A;          redo A;
2313        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2314          !!!cp (139);          !!!cp (139);
2315          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2316          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2317          ## reconsume          ## reconsume
2318    
2319          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2320    
2321          redo A;          redo A;
2322        } else {        } else {
2323          !!!cp (140);          !!!cp (140);
2324          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2325              .= chr ($self->{next_char});              .= chr ($self->{nc});
2326          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2327          !!!next-input-character;          !!!next-input-character;
2328          redo A;          redo A;
2329        }        }
2330      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2331        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2332          !!!cp (141);          !!!cp (141);
2333          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2334          !!!next-input-character;          !!!next-input-character;
2335          redo A;          redo A;
2336        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2337          !!!cp (142);          !!!cp (142);
2338          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2339          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2340          !!!next-input-character;          !!!next-input-character;
2341    
2342          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2343    
2344          redo A;          redo A;
2345        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2346          !!!cp (143);          !!!cp (143);
2347          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2348          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2349          ## reconsume          ## reconsume
2350    
2351          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2352    
2353          redo A;          redo A;
2354        } else {        } else {
2355          !!!cp (144);          !!!cp (144);
2356          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2357              .= '-' . chr ($self->{next_char});              .= '-' . chr ($self->{nc});
2358          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2359          !!!next-input-character;          !!!next-input-character;
2360          redo A;          redo A;
2361        }        }
2362      } elsif ($self->{state} == COMMENT_STATE) {      } elsif ($self->{state} == COMMENT_STATE) {
2363        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2364          !!!cp (145);          !!!cp (145);
2365          $self->{state} = COMMENT_END_DASH_STATE;          $self->{state} = COMMENT_END_DASH_STATE;
2366          !!!next-input-character;          !!!next-input-character;
2367          redo A;          redo A;
2368        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2369          !!!cp (146);          !!!cp (146);
2370          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2371          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2372          ## reconsume          ## reconsume
2373    
2374          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2375    
2376          redo A;          redo A;
2377        } else {        } else {
2378          !!!cp (147);          !!!cp (147);
2379          $self->{current_token}->{data} .= chr ($self->{next_char}); # comment          $self->{ct}->{data} .= chr ($self->{nc}); # comment
2380            $self->{read_until}->($self->{ct}->{data},
2381                                  q[-],
2382                                  length $self->{ct}->{data});
2383    
2384          ## Stay in the state          ## Stay in the state
2385          !!!next-input-character;          !!!next-input-character;
2386          redo A;          redo A;
2387        }        }
2388      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2389        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2390          !!!cp (148);          !!!cp (148);
2391          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2392          !!!next-input-character;          !!!next-input-character;
2393          redo A;          redo A;
2394        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2395          !!!cp (149);          !!!cp (149);
2396          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2397          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2398          ## reconsume          ## reconsume
2399    
2400          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2401    
2402          redo A;          redo A;
2403        } else {        } else {
2404          !!!cp (150);          !!!cp (150);
2405          $self->{current_token}->{data} .= '-' . chr ($self->{next_char}); # comment          $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2406          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2407          !!!next-input-character;          !!!next-input-character;
2408          redo A;          redo A;
2409        }        }
2410      } elsif ($self->{state} == COMMENT_END_STATE) {      } elsif ($self->{state} == COMMENT_END_STATE) {
2411        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2412          !!!cp (151);          !!!cp (151);
2413          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2414          !!!next-input-character;          !!!next-input-character;
2415    
2416          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2417    
2418          redo A;          redo A;
2419        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2420          !!!cp (152);          !!!cp (152);
2421          !!!parse-error (type => 'dash in comment',          !!!parse-error (type => 'dash in comment',
2422                          line => $self->{line_prev},                          line => $self->{line_prev},
2423                          column => $self->{column_prev});                          column => $self->{column_prev});
2424          $self->{current_token}->{data} .= '-'; # comment          $self->{ct}->{data} .= '-'; # comment
2425          ## Stay in the state          ## Stay in the state
2426          !!!next-input-character;          !!!next-input-character;
2427          redo A;          redo A;
2428        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2429          !!!cp (153);          !!!cp (153);
2430          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2431          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2432          ## reconsume          ## reconsume
2433    
2434          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2435    
2436          redo A;          redo A;
2437        } else {        } else {
# Line 1884  sub _get_next_token ($) { Line 2439  sub _get_next_token ($) {
2439          !!!parse-error (type => 'dash in comment',          !!!parse-error (type => 'dash in comment',
2440                          line => $self->{line_prev},                          line => $self->{line_prev},
2441                          column => $self->{column_prev});                          column => $self->{column_prev});
2442          $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment          $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2443          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2444          !!!next-input-character;          !!!next-input-character;
2445          redo A;          redo A;
2446        }        }
2447      } elsif ($self->{state} == DOCTYPE_STATE) {      } elsif ($self->{state} == DOCTYPE_STATE) {
2448        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  
2449          !!!cp (155);          !!!cp (155);
2450          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2451          !!!next-input-character;          !!!next-input-character;
# Line 1907  sub _get_next_token ($) { Line 2458  sub _get_next_token ($) {
2458          redo A;          redo A;
2459        }        }
2460      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2461        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  
2462          !!!cp (157);          !!!cp (157);
2463          ## Stay in the state          ## Stay in the state
2464          !!!next-input-character;          !!!next-input-character;
2465          redo A;          redo A;
2466        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2467          !!!cp (158);          !!!cp (158);
2468          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2469          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2470          !!!next-input-character;          !!!next-input-character;
2471    
2472          !!!emit ($self->{current_token}); # DOCTYPE (quirks)          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2473    
2474          redo A;          redo A;
2475        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2476          !!!cp (159);          !!!cp (159);
2477          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2478          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2479          ## reconsume          ## reconsume
2480    
2481          !!!emit ($self->{current_token}); # DOCTYPE (quirks)          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2482    
2483          redo A;          redo A;
2484        } else {        } else {
2485          !!!cp (160);          !!!cp (160);
2486          $self->{current_token}->{name} = chr $self->{next_char};          $self->{ct}->{name} = chr $self->{nc};
2487          delete $self->{current_token}->{quirks};          delete $self->{ct}->{quirks};
2488  ## ISSUE: "Set the token's name name to the" in the spec  ## ISSUE: "Set the token's name name to the" in the spec
2489          $self->{state} = DOCTYPE_NAME_STATE;          $self->{state} = DOCTYPE_NAME_STATE;
2490          !!!next-input-character;          !!!next-input-character;
# Line 1945  sub _get_next_token ($) { Line 2492  sub _get_next_token ($) {
2492        }        }
2493      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2494  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2495        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  
2496          !!!cp (161);          !!!cp (161);
2497          $self->{state} = AFTER_DOCTYPE_NAME_STATE;          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
2498          !!!next-input-character;          !!!next-input-character;
2499          redo A;          redo A;
2500        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2501          !!!cp (162);          !!!cp (162);
2502          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2503          !!!next-input-character;          !!!next-input-character;
2504    
2505          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2506    
2507          redo A;          redo A;
2508        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2509          !!!cp (163);          !!!cp (163);
2510          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2511          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2512          ## reconsume          ## reconsume
2513    
2514          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2515          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2516    
2517          redo A;          redo A;
2518        } else {        } else {
2519          !!!cp (164);          !!!cp (164);
2520          $self->{current_token}->{name}          $self->{ct}->{name}
2521            .= chr ($self->{next_char}); # DOCTYPE            .= chr ($self->{nc}); # DOCTYPE
2522          ## Stay in the state          ## Stay in the state
2523          !!!next-input-character;          !!!next-input-character;
2524          redo A;          redo A;
2525        }        }
2526      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2527        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  
2528          !!!cp (165);          !!!cp (165);
2529          ## Stay in the state          ## Stay in the state
2530          !!!next-input-character;          !!!next-input-character;
2531          redo A;          redo A;
2532        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2533          !!!cp (166);          !!!cp (166);
2534          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2535          !!!next-input-character;          !!!next-input-character;
2536    
2537          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2538    
2539          redo A;          redo A;
2540        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2541          !!!cp (167);          !!!cp (167);
2542          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2543          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2544          ## reconsume          ## reconsume
2545    
2546          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2547          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2548    
2549          redo A;          redo A;
2550        } elsif ($self->{next_char} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2551                 $self->{next_char} == 0x0070) { # p                 $self->{nc} == 0x0070) { # p
2552            $self->{state} = PUBLIC_STATE;
2553            $self->{s_kwd} = chr $self->{nc};
2554          !!!next-input-character;          !!!next-input-character;
2555          if ($self->{next_char} == 0x0055 or # U          redo A;
2556              $self->{next_char} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2557            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2558            if ($self->{next_char} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2559                $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  
2560          !!!next-input-character;          !!!next-input-character;
2561          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);  
         }  
   
         #  
2562        } else {        } else {
2563          !!!cp (180);          !!!cp (180);
2564            !!!parse-error (type => 'string after DOCTYPE name');
2565            $self->{ct}->{quirks} = 1;
2566    
2567            $self->{state} = BOGUS_DOCTYPE_STATE;
2568          !!!next-input-character;          !!!next-input-character;
2569          #          redo A;
2570        }        }
2571        } elsif ($self->{state} == PUBLIC_STATE) {
2572          ## ASCII case-insensitive
2573          if ($self->{nc} == [
2574                undef,
2575                0x0055, # U
2576                0x0042, # B
2577                0x004C, # L
2578                0x0049, # I
2579              ]->[length $self->{s_kwd}] or
2580              $self->{nc} == [
2581                undef,
2582                0x0075, # u
2583                0x0062, # b
2584                0x006C, # l
2585                0x0069, # i
2586              ]->[length $self->{s_kwd}]) {
2587            !!!cp (175);
2588            ## Stay in the state.
2589            $self->{s_kwd} .= chr $self->{nc};
2590            !!!next-input-character;
2591            redo A;
2592          } elsif ((length $self->{s_kwd}) == 5 and
2593                   ($self->{nc} == 0x0043 or # C
2594                    $self->{nc} == 0x0063)) { # c
2595            !!!cp (168);
2596            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2597            !!!next-input-character;
2598            redo A;
2599          } else {
2600            !!!cp (169);
2601            !!!parse-error (type => 'string after DOCTYPE name',
2602                            line => $self->{line_prev},
2603                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2604            $self->{ct}->{quirks} = 1;
2605    
2606        !!!parse-error (type => 'string after DOCTYPE name');          $self->{state} = BOGUS_DOCTYPE_STATE;
2607        $self->{current_token}->{quirks} = 1;          ## Reconsume.
2608            redo A;
2609          }
2610        } elsif ($self->{state} == SYSTEM_STATE) {
2611          ## ASCII case-insensitive
2612          if ($self->{nc} == [
2613                undef,
2614                0x0059, # Y
2615                0x0053, # S
2616                0x0054, # T
2617                0x0045, # E
2618              ]->[length $self->{s_kwd}] or
2619              $self->{nc} == [
2620                undef,
2621                0x0079, # y
2622                0x0073, # s
2623                0x0074, # t
2624                0x0065, # e
2625              ]->[length $self->{s_kwd}]) {
2626            !!!cp (170);
2627            ## Stay in the state.
2628            $self->{s_kwd} .= chr $self->{nc};
2629            !!!next-input-character;
2630            redo A;
2631          } elsif ((length $self->{s_kwd}) == 5 and
2632                   ($self->{nc} == 0x004D or # M
2633                    $self->{nc} == 0x006D)) { # m
2634            !!!cp (171);
2635            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2636            !!!next-input-character;
2637            redo A;
2638          } else {
2639            !!!cp (172);
2640            !!!parse-error (type => 'string after DOCTYPE name',
2641                            line => $self->{line_prev},
2642                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2643            $self->{ct}->{quirks} = 1;
2644    
2645        $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2646        # next-input-character is already done          ## Reconsume.
2647        redo A;          redo A;
2648          }
2649      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2650        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}}) {  
2651          !!!cp (181);          !!!cp (181);
2652          ## Stay in the state          ## Stay in the state
2653          !!!next-input-character;          !!!next-input-character;
2654          redo A;          redo A;
2655        } elsif ($self->{next_char} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2656          !!!cp (182);          !!!cp (182);
2657          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2658          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2659          !!!next-input-character;          !!!next-input-character;
2660          redo A;          redo A;
2661        } elsif ($self->{next_char} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2662          !!!cp (183);          !!!cp (183);
2663          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2664          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2665          !!!next-input-character;          !!!next-input-character;
2666          redo A;          redo A;
2667        } elsif ($self->{next_char} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2668          !!!cp (184);          !!!cp (184);
2669          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2670    
2671          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2672          !!!next-input-character;          !!!next-input-character;
2673    
2674          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2675          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2676    
2677          redo A;          redo A;
2678        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2679          !!!cp (185);          !!!cp (185);
2680          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2681    
2682          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2683          ## reconsume          ## reconsume
2684    
2685          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2686          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2687    
2688          redo A;          redo A;
2689        } else {        } else {
2690          !!!cp (186);          !!!cp (186);
2691          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2692          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2693    
2694          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2695          !!!next-input-character;          !!!next-input-character;
2696          redo A;          redo A;
2697        }        }
2698      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2699        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2700          !!!cp (187);          !!!cp (187);
2701          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2702          !!!next-input-character;          !!!next-input-character;
2703          redo A;          redo A;
2704        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2705          !!!cp (188);          !!!cp (188);
2706          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2707    
2708          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2709          !!!next-input-character;          !!!next-input-character;
2710    
2711          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2712          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2713    
2714          redo A;          redo A;
2715        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2716          !!!cp (189);          !!!cp (189);
2717          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2718    
2719          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2720          ## reconsume          ## reconsume
2721    
2722          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2723          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2724    
2725          redo A;          redo A;
2726        } else {        } else {
2727          !!!cp (190);          !!!cp (190);
2728          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2729              .= chr $self->{next_char};              .= chr $self->{nc};
2730            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2731                                  length $self->{ct}->{pubid});
2732    
2733          ## Stay in the state          ## Stay in the state
2734          !!!next-input-character;          !!!next-input-character;
2735          redo A;          redo A;
2736        }        }
2737      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2738        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2739          !!!cp (191);          !!!cp (191);
2740          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2741          !!!next-input-character;          !!!next-input-character;
2742          redo A;          redo A;
2743        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2744          !!!cp (192);          !!!cp (192);
2745          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2746    
2747          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2748          !!!next-input-character;          !!!next-input-character;
2749    
2750          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2751          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2752    
2753          redo A;          redo A;
2754        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2755          !!!cp (193);          !!!cp (193);
2756          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2757    
2758          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2759          ## reconsume          ## reconsume
2760    
2761          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2762          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2763    
2764          redo A;          redo A;
2765        } else {        } else {
2766          !!!cp (194);          !!!cp (194);
2767          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2768              .= chr $self->{next_char};              .= chr $self->{nc};
2769            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2770                                  length $self->{ct}->{pubid});
2771    
2772          ## Stay in the state          ## Stay in the state
2773          !!!next-input-character;          !!!next-input-character;
2774          redo A;          redo A;
2775        }        }
2776      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2777        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}}) {  
2778          !!!cp (195);          !!!cp (195);
2779          ## Stay in the state          ## Stay in the state
2780          !!!next-input-character;          !!!next-input-character;
2781          redo A;          redo A;
2782        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2783          !!!cp (196);          !!!cp (196);
2784          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2785          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2786          !!!next-input-character;          !!!next-input-character;
2787          redo A;          redo A;
2788        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2789          !!!cp (197);          !!!cp (197);
2790          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2791          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2792          !!!next-input-character;          !!!next-input-character;
2793          redo A;          redo A;
2794        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2795          !!!cp (198);          !!!cp (198);
2796          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2797          !!!next-input-character;          !!!next-input-character;
2798    
2799          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2800    
2801          redo A;          redo A;
2802        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2803          !!!cp (199);          !!!cp (199);
2804          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2805    
2806          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2807          ## reconsume          ## reconsume
2808    
2809          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2810          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2811    
2812          redo A;          redo A;
2813        } else {        } else {
2814          !!!cp (200);          !!!cp (200);
2815          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2816          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2817    
2818          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2819          !!!next-input-character;          !!!next-input-character;
2820          redo A;          redo A;
2821        }        }
2822      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2823        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}}) {  
2824          !!!cp (201);          !!!cp (201);
2825          ## Stay in the state          ## Stay in the state
2826          !!!next-input-character;          !!!next-input-character;
2827          redo A;          redo A;
2828        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2829          !!!cp (202);          !!!cp (202);
2830          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2831          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2832          !!!next-input-character;          !!!next-input-character;
2833          redo A;          redo A;
2834        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2835          !!!cp (203);          !!!cp (203);
2836          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2837          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2838          !!!next-input-character;          !!!next-input-character;
2839          redo A;          redo A;
2840        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2841          !!!cp (204);          !!!cp (204);
2842          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2843          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2844          !!!next-input-character;          !!!next-input-character;
2845    
2846          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2847          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2848    
2849          redo A;          redo A;
2850        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2851          !!!cp (205);          !!!cp (205);
2852          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2853    
2854          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2855          ## reconsume          ## reconsume
2856    
2857          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2858          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2859    
2860          redo A;          redo A;
2861        } else {        } else {
2862          !!!cp (206);          !!!cp (206);
2863          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2864          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2865    
2866          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2867          !!!next-input-character;          !!!next-input-character;
2868          redo A;          redo A;
2869        }        }
2870      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2871        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2872          !!!cp (207);          !!!cp (207);
2873          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2874          !!!next-input-character;          !!!next-input-character;
2875          redo A;          redo A;
2876        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2877          !!!cp (208);          !!!cp (208);
2878          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2879    
2880          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2881          !!!next-input-character;          !!!next-input-character;
2882    
2883          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2884          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2885    
2886          redo A;          redo A;
2887        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2888          !!!cp (209);          !!!cp (209);
2889          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2890    
2891          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2892          ## reconsume          ## reconsume
2893    
2894          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2895          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2896    
2897          redo A;          redo A;
2898        } else {        } else {
2899          !!!cp (210);          !!!cp (210);
2900          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2901              .= chr $self->{next_char};              .= chr $self->{nc};
2902            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2903                                  length $self->{ct}->{sysid});
2904    
2905          ## Stay in the state          ## Stay in the state
2906          !!!next-input-character;          !!!next-input-character;
2907          redo A;          redo A;
2908        }        }
2909      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2910        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2911          !!!cp (211);          !!!cp (211);
2912          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2913          !!!next-input-character;          !!!next-input-character;
2914          redo A;          redo A;
2915        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2916          !!!cp (212);          !!!cp (212);
2917          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2918    
2919          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2920          !!!next-input-character;          !!!next-input-character;
2921    
2922          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2923          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2924    
2925          redo A;          redo A;
2926        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2927          !!!cp (213);          !!!cp (213);
2928          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2929    
2930          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2931          ## reconsume          ## reconsume
2932    
2933          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2934          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2935    
2936          redo A;          redo A;
2937        } else {        } else {
2938          !!!cp (214);          !!!cp (214);
2939          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2940              .= chr $self->{next_char};              .= chr $self->{nc};
2941            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2942                                  length $self->{ct}->{sysid});
2943    
2944          ## Stay in the state          ## Stay in the state
2945          !!!next-input-character;          !!!next-input-character;
2946          redo A;          redo A;
2947        }        }
2948      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2949        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}}) {  
2950          !!!cp (215);          !!!cp (215);
2951          ## Stay in the state          ## Stay in the state
2952          !!!next-input-character;          !!!next-input-character;
2953          redo A;          redo A;
2954        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2955          !!!cp (216);          !!!cp (216);
2956          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2957          !!!next-input-character;          !!!next-input-character;
2958    
2959          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2960    
2961          redo A;          redo A;
2962        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2963          !!!cp (217);          !!!cp (217);
2964          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
   
2965          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2966          ## reconsume          ## reconsume
2967    
2968          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2969          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2970    
2971          redo A;          redo A;
2972        } else {        } else {
2973          !!!cp (218);          !!!cp (218);
2974          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2975          #$self->{current_token}->{quirks} = 1;          #$self->{ct}->{quirks} = 1;
2976    
2977          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2978          !!!next-input-character;          !!!next-input-character;
2979          redo A;          redo A;
2980        }        }
2981      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2982        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2983          !!!cp (219);          !!!cp (219);
2984          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2985          !!!next-input-character;          !!!next-input-character;
2986    
2987          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2988    
2989          redo A;          redo A;
2990        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2991          !!!cp (220);          !!!cp (220);
2992          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2993          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2994          ## reconsume          ## reconsume
2995    
2996          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2997    
2998          redo A;          redo A;
2999        } else {        } else {
3000          !!!cp (221);          !!!cp (221);
3001            my $s = '';
3002            $self->{read_until}->($s, q[>], 0);
3003    
3004          ## Stay in the state          ## Stay in the state
3005          !!!next-input-character;          !!!next-input-character;
3006          redo A;          redo A;
3007        }        }
3008      } elsif ($self->{state} == CDATA_BLOCK_STATE) {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
3009        my $s = '';        ## NOTE: "CDATA section state" in the state is jointly implemented
3010          ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3011          ## and |CDATA_SECTION_MSE2_STATE|.
3012                
3013        my ($l, $c) = ($self->{line}, $self->{column});        if ($self->{nc} == 0x005D) { # ]
3014            !!!cp (221.1);
3015        CS: while ($self->{next_char} != -1) {          $self->{state} = CDATA_SECTION_MSE1_STATE;
         if ($self->{next_char} == 0x005D) { # ]  
           !!!next-input-character;  
           if ($self->{next_char} == 0x005D) { # ]  
             !!!next-input-character;  
             MDC: {  
               if ($self->{next_char} == 0x003E) { # >  
                 !!!cp (221.1);  
                 !!!next-input-character;  
                 last CS;  
               } elsif ($self->{next_char} == 0x005D) { # ]  
                 !!!cp (221.2);  
                 $s .= ']';  
                 !!!next-input-character;  
                 redo MDC;  
               } else {  
                 !!!cp (221.3);  
                 $s .= ']]';  
                 #  
               }  
             } # MDC  
           } else {  
             !!!cp (221.4);  
             $s .= ']';  
             #  
           }  
         } else {  
           !!!cp (221.5);  
           #  
         }  
         $s .= chr $self->{next_char};  
3016          !!!next-input-character;          !!!next-input-character;
3017        } # CS          redo A;
3018          } elsif ($self->{nc} == -1) {
3019            $self->{state} = DATA_STATE;
3020            !!!next-input-character;
3021            if (length $self->{ct}->{data}) { # character
3022              !!!cp (221.2);
3023              !!!emit ($self->{ct}); # character
3024            } else {
3025              !!!cp (221.3);
3026              ## No token to emit. $self->{ct} is discarded.
3027            }        
3028            redo A;
3029          } else {
3030            !!!cp (221.4);
3031            $self->{ct}->{data} .= chr $self->{nc};
3032            $self->{read_until}->($self->{ct}->{data},
3033                                  q<]>,
3034                                  length $self->{ct}->{data});
3035    
3036        $self->{state} = DATA_STATE;          ## Stay in the state.
3037        ## next-input-character done or EOF, which is reconsumed.          !!!next-input-character;
3038            redo A;
3039          }
3040    
3041        if (length $s) {        ## ISSUE: "text tokens" in spec.
3042        } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3043          if ($self->{nc} == 0x005D) { # ]
3044            !!!cp (221.5);
3045            $self->{state} = CDATA_SECTION_MSE2_STATE;
3046            !!!next-input-character;
3047            redo A;
3048          } else {
3049          !!!cp (221.6);          !!!cp (221.6);
3050          !!!emit ({type => CHARACTER_TOKEN, data => $s,          $self->{ct}->{data} .= ']';
3051                    line => $l, column => $c});          $self->{state} = CDATA_SECTION_STATE;
3052            ## Reconsume.
3053            redo A;
3054          }
3055        } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3056          if ($self->{nc} == 0x003E) { # >
3057            $self->{state} = DATA_STATE;
3058            !!!next-input-character;
3059            if (length $self->{ct}->{data}) { # character
3060              !!!cp (221.7);
3061              !!!emit ($self->{ct}); # character
3062            } else {
3063              !!!cp (221.8);
3064              ## No token to emit. $self->{ct} is discarded.
3065            }
3066            redo A;
3067          } elsif ($self->{nc} == 0x005D) { # ]
3068            !!!cp (221.9); # character
3069            $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3070            ## Stay in the state.
3071            !!!next-input-character;
3072            redo A;
3073        } else {        } else {
3074          !!!cp (221.7);          !!!cp (221.11);
3075            $self->{ct}->{data} .= ']]'; # character
3076            $self->{state} = CDATA_SECTION_STATE;
3077            ## Reconsume.
3078            redo A;
3079          }
3080        } elsif ($self->{state} == ENTITY_STATE) {
3081          if ($is_space->{$self->{nc}} or
3082              {
3083                0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3084                $self->{entity_add} => 1,
3085              }->{$self->{nc}}) {
3086            !!!cp (1001);
3087            ## Don't consume
3088            ## No error
3089            ## Return nothing.
3090            #
3091          } elsif ($self->{nc} == 0x0023) { # #
3092            !!!cp (999);
3093            $self->{state} = ENTITY_HASH_STATE;
3094            $self->{s_kwd} = '#';
3095            !!!next-input-character;
3096            redo A;
3097          } elsif ((0x0041 <= $self->{nc} and
3098                    $self->{nc} <= 0x005A) or # A..Z
3099                   (0x0061 <= $self->{nc} and
3100                    $self->{nc} <= 0x007A)) { # a..z
3101            !!!cp (998);
3102            require Whatpm::_NamedEntityList;
3103            $self->{state} = ENTITY_NAME_STATE;
3104            $self->{s_kwd} = chr $self->{nc};
3105            $self->{entity__value} = $self->{s_kwd};
3106            $self->{entity__match} = 0;
3107            !!!next-input-character;
3108            redo A;
3109          } else {
3110            !!!cp (1027);
3111            !!!parse-error (type => 'bare ero');
3112            ## Return nothing.
3113            #
3114        }        }
3115    
3116        redo A;        ## NOTE: No character is consumed by the "consume a character
3117          ## reference" algorithm.  In other word, there is an "&" character
3118        ## ISSUE: "text tokens" in spec.        ## that does not introduce a character reference, which would be
3119        ## TODO: Streaming support        ## appended to the parent element or the attribute value in later
3120      } else {        ## process of the tokenizer.
3121        die "$0: $self->{state}: Unknown state";  
3122      }        if ($self->{prev_state} == DATA_STATE) {
3123    } # A            !!!cp (997);
3124            $self->{state} = $self->{prev_state};
3125    die "$0: _get_next_token: unexpected case";          ## Reconsume.
3126  } # _get_next_token          !!!emit ({type => CHARACTER_TOKEN, data => '&',
3127                      line => $self->{line_prev},
3128  sub _tokenize_attempt_to_consume_an_entity ($$$) {                    column => $self->{column_prev},
3129    my ($self, $in_attr, $additional) = @_;                   });
3130            redo A;
3131    my ($l, $c) = ($self->{line_prev}, $self->{column_prev});        } else {
3132            !!!cp (996);
3133            $self->{ca}->{value} .= '&';
3134            $self->{state} = $self->{prev_state};
3135            ## Reconsume.
3136            redo A;
3137          }
3138        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3139          if ($self->{nc} == 0x0078 or # x
3140              $self->{nc} == 0x0058) { # X
3141            !!!cp (995);
3142            $self->{state} = HEXREF_X_STATE;
3143            $self->{s_kwd} .= chr $self->{nc};
3144            !!!next-input-character;
3145            redo A;
3146          } elsif (0x0030 <= $self->{nc} and
3147                   $self->{nc} <= 0x0039) { # 0..9
3148            !!!cp (994);
3149            $self->{state} = NCR_NUM_STATE;
3150            $self->{s_kwd} = $self->{nc} - 0x0030;
3151            !!!next-input-character;
3152            redo A;
3153          } else {
3154            !!!parse-error (type => 'bare nero',
3155                            line => $self->{line_prev},
3156                            column => $self->{column_prev} - 1);
3157    
3158    if ({          ## NOTE: According to the spec algorithm, nothing is returned,
3159         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,          ## and then "&#" is appended to the parent element or the attribute
3160         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR          ## value in the later processing.
3161         $additional => 1,  
3162        }->{$self->{next_char}}) {          if ($self->{prev_state} == DATA_STATE) {
3163      !!!cp (1001);            !!!cp (1019);
3164      ## Don't consume            $self->{state} = $self->{prev_state};
3165      ## No error            ## Reconsume.
3166      return undef;            !!!emit ({type => CHARACTER_TOKEN,
3167    } elsif ($self->{next_char} == 0x0023) { # #                      data => '&#',
3168      !!!next-input-character;                      line => $self->{line_prev},
3169      if ($self->{next_char} == 0x0078 or # x                      column => $self->{column_prev} - 1,
3170          $self->{next_char} == 0x0058) { # X                     });
3171        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;  
3172          } else {          } else {
3173            !!!cp (1007);            !!!cp (993);
3174            !!!parse-error (type => 'no refc', line => $l, column => $c);            $self->{ca}->{value} .= '&#';
3175              $self->{state} = $self->{prev_state};
3176              ## Reconsume.
3177              redo A;
3178          }          }
3179          }
3180          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {      } elsif ($self->{state} == NCR_NUM_STATE) {
3181            !!!cp (1008);        if (0x0030 <= $self->{nc} and
3182            !!!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  
3183          !!!cp (1012);          !!!cp (1012);
3184          $code *= 10;          $self->{s_kwd} *= 10;
3185          $code += $self->{next_char} - 0x0030;          $self->{s_kwd} += $self->{nc} - 0x0030;
3186                    
3187            ## Stay in the state.
3188          !!!next-input-character;          !!!next-input-character;
3189        }          redo A;
3190          } elsif ($self->{nc} == 0x003B) { # ;
       if ($self->{next_char} == 0x003B) { # ;  
3191          !!!cp (1013);          !!!cp (1013);
3192          !!!next-input-character;          !!!next-input-character;
3193            #
3194        } else {        } else {
3195          !!!cp (1014);          !!!cp (1014);
3196          !!!parse-error (type => 'no refc', line => $l, column => $c);          !!!parse-error (type => 'no refc');
3197            ## Reconsume.
3198            #
3199        }        }
3200    
3201        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3202          my $l = $self->{line_prev};
3203          my $c = $self->{column_prev};
3204          if ($charref_map->{$code}) {
3205          !!!cp (1015);          !!!cp (1015);
3206          !!!parse-error (type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c);          !!!parse-error (type => 'invalid character reference',
3207          $code = 0xFFFD;                          text => (sprintf 'U+%04X', $code),
3208                            line => $l, column => $c);
3209            $code = $charref_map->{$code};
3210        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3211          !!!cp (1016);          !!!cp (1016);
3212          !!!parse-error (type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c);          !!!parse-error (type => 'invalid character reference',
3213                            text => (sprintf 'U-%08X', $code),
3214                            line => $l, column => $c);
3215          $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};  
3216        }        }
3217          
3218        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1,        if ($self->{prev_state} == DATA_STATE) {
3219                line => $l, column => $c,          !!!cp (992);
3220               };          $self->{state} = $self->{prev_state};
3221      } else {          ## Reconsume.
3222        !!!cp (1019);          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3223        !!!parse-error (type => 'bare nero', line => $l, column => $c);                    line => $l, column => $c,
3224        !!!back-next-input-character ($self->{next_char});                   });
3225        $self->{next_char} = 0x0023; # #          redo A;
3226        return undef;        } else {
3227      }          !!!cp (991);
3228    } elsif ((0x0041 <= $self->{next_char} and          $self->{ca}->{value} .= chr $code;
3229              $self->{next_char} <= 0x005A) or          $self->{ca}->{has_reference} = 1;
3230             (0x0061 <= $self->{next_char} and          $self->{state} = $self->{prev_state};
3231              $self->{next_char} <= 0x007A)) {          ## Reconsume.
3232      my $entity_name = chr $self->{next_char};          redo A;
3233      !!!next-input-character;        }
3234        } elsif ($self->{state} == HEXREF_X_STATE) {
3235      my $value = $entity_name;        if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3236      my $match = 0;            (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3237      require Whatpm::_NamedEntityList;            (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3238      our $EntityChar;          # 0..9, A..F, a..f
3239            !!!cp (990);
3240      while (length $entity_name < 10 and          $self->{state} = HEXREF_HEX_STATE;
3241             ## NOTE: Some number greater than the maximum length of entity name          $self->{s_kwd} = 0;
3242             ((0x0041 <= $self->{next_char} and # a          ## Reconsume.
3243               $self->{next_char} <= 0x005A) or # x          redo A;
3244              (0x0061 <= $self->{next_char} and # a        } else {
3245               $self->{next_char} <= 0x007A) or # z          !!!parse-error (type => 'bare hcro',
3246              (0x0030 <= $self->{next_char} and # 0                          line => $self->{line_prev},
3247               $self->{next_char} <= 0x0039) or # 9                          column => $self->{column_prev} - 2);
3248              $self->{next_char} == 0x003B)) { # ;  
3249        $entity_name .= chr $self->{next_char};          ## NOTE: According to the spec algorithm, nothing is returned,
3250        if (defined $EntityChar->{$entity_name}) {          ## and then "&#" followed by "X" or "x" is appended to the parent
3251          if ($self->{next_char} == 0x003B) { # ;          ## element or the attribute value in the later processing.
3252            !!!cp (1020);  
3253            $value = $EntityChar->{$entity_name};          if ($self->{prev_state} == DATA_STATE) {
3254            $match = 1;            !!!cp (1005);
3255            !!!next-input-character;            $self->{state} = $self->{prev_state};
3256            last;            ## Reconsume.
3257              !!!emit ({type => CHARACTER_TOKEN,
3258                        data => '&' . $self->{s_kwd},
3259                        line => $self->{line_prev},
3260                        column => $self->{column_prev} - length $self->{s_kwd},
3261                       });
3262              redo A;
3263          } else {          } else {
3264            !!!cp (1021);            !!!cp (989);
3265            $value = $EntityChar->{$entity_name};            $self->{ca}->{value} .= '&' . $self->{s_kwd};
3266            $match = -1;            $self->{state} = $self->{prev_state};
3267              ## Reconsume.
3268              redo A;
3269            }
3270          }
3271        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3272          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3273            # 0..9
3274            !!!cp (1002);
3275            $self->{s_kwd} *= 0x10;
3276            $self->{s_kwd} += $self->{nc} - 0x0030;
3277            ## Stay in the state.
3278            !!!next-input-character;
3279            redo A;
3280          } elsif (0x0061 <= $self->{nc} and
3281                   $self->{nc} <= 0x0066) { # a..f
3282            !!!cp (1003);
3283            $self->{s_kwd} *= 0x10;
3284            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3285            ## Stay in the state.
3286            !!!next-input-character;
3287            redo A;
3288          } elsif (0x0041 <= $self->{nc} and
3289                   $self->{nc} <= 0x0046) { # A..F
3290            !!!cp (1004);
3291            $self->{s_kwd} *= 0x10;
3292            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3293            ## Stay in the state.
3294            !!!next-input-character;
3295            redo A;
3296          } elsif ($self->{nc} == 0x003B) { # ;
3297            !!!cp (1006);
3298            !!!next-input-character;
3299            #
3300          } else {
3301            !!!cp (1007);
3302            !!!parse-error (type => 'no refc',
3303                            line => $self->{line},
3304                            column => $self->{column});
3305            ## Reconsume.
3306            #
3307          }
3308    
3309          my $code = $self->{s_kwd};
3310          my $l = $self->{line_prev};
3311          my $c = $self->{column_prev};
3312          if ($charref_map->{$code}) {
3313            !!!cp (1008);
3314            !!!parse-error (type => 'invalid character reference',
3315                            text => (sprintf 'U+%04X', $code),
3316                            line => $l, column => $c);
3317            $code = $charref_map->{$code};
3318          } elsif ($code > 0x10FFFF) {
3319            !!!cp (1009);
3320            !!!parse-error (type => 'invalid character reference',
3321                            text => (sprintf 'U-%08X', $code),
3322                            line => $l, column => $c);
3323            $code = 0xFFFD;
3324          }
3325    
3326          if ($self->{prev_state} == DATA_STATE) {
3327            !!!cp (988);
3328            $self->{state} = $self->{prev_state};
3329            ## Reconsume.
3330            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3331                      line => $l, column => $c,
3332                     });
3333            redo A;
3334          } else {
3335            !!!cp (987);
3336            $self->{ca}->{value} .= chr $code;
3337            $self->{ca}->{has_reference} = 1;
3338            $self->{state} = $self->{prev_state};
3339            ## Reconsume.
3340            redo A;
3341          }
3342        } elsif ($self->{state} == ENTITY_NAME_STATE) {
3343          if (length $self->{s_kwd} < 30 and
3344              ## NOTE: Some number greater than the maximum length of entity name
3345              ((0x0041 <= $self->{nc} and # a
3346                $self->{nc} <= 0x005A) or # x
3347               (0x0061 <= $self->{nc} and # a
3348                $self->{nc} <= 0x007A) or # z
3349               (0x0030 <= $self->{nc} and # 0
3350                $self->{nc} <= 0x0039) or # 9
3351               $self->{nc} == 0x003B)) { # ;
3352            our $EntityChar;
3353            $self->{s_kwd} .= chr $self->{nc};
3354            if (defined $EntityChar->{$self->{s_kwd}}) {
3355              if ($self->{nc} == 0x003B) { # ;
3356                !!!cp (1020);
3357                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3358                $self->{entity__match} = 1;
3359                !!!next-input-character;
3360                #
3361              } else {
3362                !!!cp (1021);
3363                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3364                $self->{entity__match} = -1;
3365                ## Stay in the state.
3366                !!!next-input-character;
3367                redo A;
3368              }
3369            } else {
3370              !!!cp (1022);
3371              $self->{entity__value} .= chr $self->{nc};
3372              $self->{entity__match} *= 2;
3373              ## Stay in the state.
3374            !!!next-input-character;            !!!next-input-character;
3375              redo A;
3376            }
3377          }
3378    
3379          my $data;
3380          my $has_ref;
3381          if ($self->{entity__match} > 0) {
3382            !!!cp (1023);
3383            $data = $self->{entity__value};
3384            $has_ref = 1;
3385            #
3386          } elsif ($self->{entity__match} < 0) {
3387            !!!parse-error (type => 'no refc');
3388            if ($self->{prev_state} != DATA_STATE and # in attribute
3389                $self->{entity__match} < -1) {
3390              !!!cp (1024);
3391              $data = '&' . $self->{s_kwd};
3392              #
3393            } else {
3394              !!!cp (1025);
3395              $data = $self->{entity__value};
3396              $has_ref = 1;
3397              #
3398          }          }
3399        } else {        } else {
3400          !!!cp (1022);          !!!cp (1026);
3401          $value .= chr $self->{next_char};          !!!parse-error (type => 'bare ero',
3402          $match *= 2;                          line => $self->{line_prev},
3403          !!!next-input-character;                          column => $self->{column_prev} - length $self->{s_kwd});
3404            $data = '&' . $self->{s_kwd};
3405            #
3406        }        }
3407      }    
3408              ## NOTE: In these cases, when a character reference is found,
3409      if ($match > 0) {        ## it is consumed and a character token is returned, or, otherwise,
3410        !!!cp (1023);        ## nothing is consumed and returned, according to the spec algorithm.
3411        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,        ## In this implementation, anything that has been examined by the
3412                line => $l, column => $c,        ## tokenizer is appended to the parent element or the attribute value
3413               };        ## as string, either literal string when no character reference or
3414      } elsif ($match < 0) {        ## entity-replaced string otherwise, in this stage, since any characters
3415        !!!parse-error (type => 'no refc', line => $l, column => $c);        ## that would not be consumed are appended in the data state or in an
3416        if ($in_attr and $match < -1) {        ## appropriate attribute value state anyway.
3417          !!!cp (1024);  
3418          return {type => CHARACTER_TOKEN, data => '&'.$entity_name,        if ($self->{prev_state} == DATA_STATE) {
3419                  line => $l, column => $c,          !!!cp (986);
3420                 };          $self->{state} = $self->{prev_state};
3421        } else {          ## Reconsume.
3422          !!!cp (1025);          !!!emit ({type => CHARACTER_TOKEN,
3423          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,                    data => $data,
3424                  line => $l, column => $c,                    line => $self->{line_prev},
3425                 };                    column => $self->{column_prev} + 1 - length $self->{s_kwd},
3426                     });
3427            redo A;
3428          } else {
3429            !!!cp (985);
3430            $self->{ca}->{value} .= $data;
3431            $self->{ca}->{has_reference} = 1 if $has_ref;
3432            $self->{state} = $self->{prev_state};
3433            ## Reconsume.
3434            redo A;
3435        }        }
3436      } else {      } else {
3437        !!!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,  
              };  
3438      }      }
3439    } else {    } # A  
3440      !!!cp (1027);  
3441      ## no characters are consumed    die "$0: _get_next_token: unexpected case";
3442      !!!parse-error (type => 'bare ero', line => $l, column => $c);  } # _get_next_token
     return undef;  
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3443    
3444  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3445    my $self = shift;    my $self = shift;
# Line 2729  sub _initialize_tree_constructor ($) { Line 3448  sub _initialize_tree_constructor ($) {
3448    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3449    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3450    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3451      $self->{document}->set_user_data (manakai_source_line => 1);
3452      $self->{document}->set_user_data (manakai_source_column => 1);
3453  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3454    
3455  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 2783  sub _tree_construction_initial ($) { Line 3504  sub _tree_construction_initial ($) {
3504        ## language.        ## language.
3505        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
3506        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3507        $doctype_name =~ tr/a-z/A-Z/;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3508        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3509            defined $token->{public_identifier} or            defined $token->{sysid}) {
           defined $token->{system_identifier}) {  
3510          !!!cp ('t1');          !!!cp ('t1');
3511          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
3512        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3513          !!!cp ('t2');          !!!cp ('t2');
         ## ISSUE: ASCII case-insensitive? (in fact it does not matter)  
3514          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
3515          } elsif (defined $token->{pubid}) {
3516            if ($token->{pubid} eq 'XSLT-compat') {
3517              !!!cp ('t1.2');
3518              !!!parse-error (type => 'XSLT-compat', token => $token,
3519                              level => $self->{level}->{should});
3520            } else {
3521              !!!parse-error (type => 'not HTML5', token => $token);
3522            }
3523        } else {        } else {
3524          !!!cp ('t3');          !!!cp ('t3');
3525            #
3526        }        }
3527                
3528        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
3529          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3530        ## NOTE: Default value for both |public_id| and |system_id| attributes        ## NOTE: Default value for both |public_id| and |system_id| attributes
3531        ## are empty strings, so that we don't set any value in missing cases.        ## are empty strings, so that we don't set any value in missing cases.
3532        $doctype->public_id ($token->{public_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3533            if defined $token->{public_identifier};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
       $doctype->system_id ($token->{system_identifier})  
           if defined $token->{system_identifier};  
3534        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3535        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3536        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
# Line 2812  sub _tree_construction_initial ($) { Line 3538  sub _tree_construction_initial ($) {
3538        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3539          !!!cp ('t4');          !!!cp ('t4');
3540          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3541        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3542          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3543          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3544          if ({          my $prefix = [
3545            "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3546            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3547            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3548            "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 1//",
3549            "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 2//",
3550            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3551            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3552            "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT//",
3553            "-//IETF//DTD HTML 2.0//EN" => 1,            "-//IETF//DTD HTML 2.0//",
3554            "-//IETF//DTD HTML 2.1E//EN" => 1,            "-//IETF//DTD HTML 2.1E//",
3555            "-//IETF//DTD HTML 3.0//EN" => 1,            "-//IETF//DTD HTML 3.0//",
3556            "-//IETF//DTD HTML 3.0//EN//" => 1,            "-//IETF//DTD HTML 3.2 FINAL//",
3557            "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,            "-//IETF//DTD HTML 3.2//",
3558            "-//IETF//DTD HTML 3.2//EN" => 1,            "-//IETF//DTD HTML 3//",
3559            "-//IETF//DTD HTML 3//EN" => 1,            "-//IETF//DTD HTML LEVEL 0//",
3560            "-//IETF//DTD HTML LEVEL 0//EN" => 1,            "-//IETF//DTD HTML LEVEL 1//",
3561            "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,            "-//IETF//DTD HTML LEVEL 2//",
3562            "-//IETF//DTD HTML LEVEL 1//EN" => 1,            "-//IETF//DTD HTML LEVEL 3//",
3563            "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 0//",
3564            "-//IETF//DTD HTML LEVEL 2//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 1//",
3565            "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 2//",
3566            "-//IETF//DTD HTML LEVEL 3//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 3//",
3567            "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,            "-//IETF//DTD HTML STRICT//",
3568            "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,            "-//IETF//DTD HTML//",
3569            "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,            "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3570            "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3571            "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3572            "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3573            "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3574            "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3575            "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3576            "-//IETF//DTD HTML STRICT//EN" => 1,            "-//NETSCAPE COMM. CORP.//DTD HTML//",
3577            "-//IETF//DTD HTML STRICT//EN//2.0" => 1,            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3578            "-//IETF//DTD HTML STRICT//EN//3.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3579            "-//IETF//DTD HTML//EN" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3580            "-//IETF//DTD HTML//EN//2.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3581            "-//IETF//DTD HTML//EN//3.0" => 1,            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3582            "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3583            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3584            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3585            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3586            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3587            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,            "-//W3C//DTD HTML 3 1995-03-24//",
3588            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,            "-//W3C//DTD HTML 3.2 DRAFT//",
3589            "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,            "-//W3C//DTD HTML 3.2 FINAL//",
3590            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,            "-//W3C//DTD HTML 3.2//",
3591            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,            "-//W3C//DTD HTML 3.2S DRAFT//",
3592            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 FRAMESET//",
3593            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3594            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3595            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3596            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,            "-//W3C//DTD W3 HTML//",
3597            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,            "-//W3O//DTD W3 HTML 3.0//",
3598            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3599            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML//",
3600            "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,          ]; # $prefix
3601            "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,          my $match;
3602            "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,          for (@$prefix) {
3603            "-//W3C//DTD HTML 3.2//EN" => 1,            if (substr ($prefix, 0, length $_) eq $_) {
3604            "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,              $match = 1;
3605            "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,              last;
3606            "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,            }
3607            "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,          }
3608            "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,          if ($match or
3609            "-//W3C//DTD W3 HTML//EN" => 1,              $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3610            "-//W3O//DTD W3 HTML 3.0//EN" => 1,              $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3611            "-//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}) {  
3612            !!!cp ('t5');            !!!cp ('t5');
3613            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3614          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3615                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3616            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3617              !!!cp ('t6');              !!!cp ('t6');
3618              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3619            } else {            } else {
3620              !!!cp ('t7');              !!!cp ('t7');
3621              $self->{document}->manakai_compat_mode ('limited quirks');              $self->{document}->manakai_compat_mode ('limited quirks');
3622            }            }
3623          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3624                   $pubid eq "-//W3C//DTD XHTML 1.0 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3625            !!!cp ('t8');            !!!cp ('t8');
3626            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3627          } else {          } else {
# Line 2910  sub _tree_construction_initial ($) { Line 3630  sub _tree_construction_initial ($) {
3630        } else {        } else {
3631          !!!cp ('t10');          !!!cp ('t10');
3632        }        }
3633        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3634          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3635          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3636          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") {
3637            ## TODO: Check the spec: PUBLIC "(limited quirks)" "(quirks)"            ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3638              ## marked as quirks.
3639            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3640            !!!cp ('t11');            !!!cp ('t11');
3641          } else {          } else {
# Line 2940  sub _tree_construction_initial ($) { Line 3661  sub _tree_construction_initial ($) {
3661        !!!ack-later;        !!!ack-later;
3662        return;        return;
3663      } elsif ($token->{type} == CHARACTER_TOKEN) {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3664        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3665          ## Ignore the token          ## Ignore the token
3666    
3667          unless (length $token->{data}) {          unless (length $token->{data}) {
# Line 2997  sub _tree_construction_root_element ($) Line 3718  sub _tree_construction_root_element ($)
3718          !!!next-token;          !!!next-token;
3719          redo B;          redo B;
3720        } elsif ($token->{type} == CHARACTER_TOKEN) {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3721          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3722            ## Ignore the token.            ## Ignore the token.
3723    
3724            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 3086  sub _reset_insertion_mode ($) { Line 3807  sub _reset_insertion_mode ($) {
3807        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3808          $last = 1;          $last = 1;
3809          if (defined $self->{inner_html_node}) {          if (defined $self->{inner_html_node}) {
3810            if ($self->{inner_html_node}->[1] & TABLE_CELL_EL) {            !!!cp ('t28');
3811              !!!cp ('t27');            $node = $self->{inner_html_node};
3812              #          } else {
3813            } else {            die "_reset_insertion_mode: t27";
             !!!cp ('t28');  
             $node = $self->{inner_html_node};  
           }  
3814          }          }
3815        }        }
3816              
3817      ## Step 4..14        ## Step 4..14
3818      my $new_mode;        my $new_mode;
3819      if ($node->[1] & FOREIGN_EL) {        if ($node->[1] & FOREIGN_EL) {
3820        ## NOTE: Strictly spaking, the line below only applies to MathML and          !!!cp ('t28.1');
3821        ## SVG elements.  Currently the HTML syntax supports only MathML and          ## NOTE: Strictly spaking, the line below only applies to MathML and
3822        ## SVG elements as foreigners.          ## SVG elements.  Currently the HTML syntax supports only MathML and
3823        $new_mode = $self->{insertion_mode} | IN_FOREIGN_CONTENT_IM;          ## SVG elements as foreigners.
3824        ## ISSUE: What is set as the secondary insertion mode?          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3825      } else {        } elsif ($node->[1] & TABLE_CELL_EL) {
3826        $new_mode = {          if ($last) {
3827              !!!cp ('t28.2');
3828              #
3829            } else {
3830              !!!cp ('t28.3');
3831              $new_mode = IN_CELL_IM;
3832            }
3833          } else {
3834            !!!cp ('t28.4');
3835            $new_mode = {
3836                        select => IN_SELECT_IM,                        select => IN_SELECT_IM,
3837                        ## NOTE: |option| and |optgroup| do not set                        ## NOTE: |option| and |optgroup| do not set
3838                        ## insertion mode to "in select" by themselves.                        ## insertion mode to "in select" by themselves.
                       td => IN_CELL_IM,  
                       th => IN_CELL_IM,  
3839                        tr => IN_ROW_IM,                        tr => IN_ROW_IM,
3840                        tbody => IN_TABLE_BODY_IM,                        tbody => IN_TABLE_BODY_IM,
3841                        thead => IN_TABLE_BODY_IM,                        thead => IN_TABLE_BODY_IM,
# Line 3122  sub _reset_insertion_mode ($) { Line 3847  sub _reset_insertion_mode ($) {
3847                        body => IN_BODY_IM,                        body => IN_BODY_IM,
3848                        frameset => IN_FRAMESET_IM,                        frameset => IN_FRAMESET_IM,
3849                       }->{$node->[0]->manakai_local_name};                       }->{$node->[0]->manakai_local_name};
3850      }        }
3851      $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3852                
3853        ## Step 15        ## Step 15
3854        if ($node->[1] & HTML_EL) {        if ($node->[1] & HTML_EL) {
# Line 3297  sub _tree_construction_main ($) { Line 4022  sub _tree_construction_main ($) {
4022        ## NOTE: An end-of-file token.        ## NOTE: An end-of-file token.
4023        if ($content_model_flag == CDATA_CONTENT_MODEL) {        if ($content_model_flag == CDATA_CONTENT_MODEL) {
4024          !!!cp ('t43');          !!!cp ('t43');
4025          !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token);          !!!parse-error (type => 'in CDATA:#eof', token => $token);
4026        } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {        } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
4027          !!!cp ('t44');          !!!cp ('t44');
4028          !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token);          !!!parse-error (type => 'in RCDATA:#eof', token => $token);
4029        } else {        } else {
4030          die "$0: $content_model_flag in parse_rcdata";          die "$0: $content_model_flag in parse_rcdata";
4031        }        }
# Line 3337  sub _tree_construction_main ($) { Line 4062  sub _tree_construction_main ($) {
4062        ## Ignore the token        ## Ignore the token
4063      } else {      } else {
4064        !!!cp ('t48');        !!!cp ('t48');
4065        !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token);        !!!parse-error (type => 'in CDATA:#eof', token => $token);
4066        ## ISSUE: And ignore?        ## ISSUE: And ignore?
4067        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4068      }      }
# Line 3388  sub _tree_construction_main ($) { Line 4113  sub _tree_construction_main ($) {
4113        } # AFE        } # AFE
4114        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4115          !!!cp ('t53');          !!!cp ('t53');
4116          !!!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);
4117          ## Ignore the token          ## Ignore the token
4118          !!!next-token;          !!!next-token;
4119          return;          return;
# Line 3405  sub _tree_construction_main ($) { Line 4130  sub _tree_construction_main ($) {
4130              last INSCOPE;              last INSCOPE;
4131            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4132              !!!cp ('t55');              !!!cp ('t55');
4133              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},              !!!parse-error (type => 'unmatched end tag',
4134                                text => $token->{tag_name},
4135                              token => $end_tag_token);                              token => $end_tag_token);
4136              ## Ignore the token              ## Ignore the token
4137              !!!next-token;              !!!next-token;
# Line 3418  sub _tree_construction_main ($) { Line 4144  sub _tree_construction_main ($) {
4144        } # INSCOPE        } # INSCOPE
4145        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4146          !!!cp ('t57');          !!!cp ('t57');
4147          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},          !!!parse-error (type => 'unmatched end tag',
4148                            text => $token->{tag_name},
4149                          token => $end_tag_token);                          token => $end_tag_token);
4150          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4151          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
# Line 3427  sub _tree_construction_main ($) { Line 4154  sub _tree_construction_main ($) {
4154        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4155          !!!cp ('t58');          !!!cp ('t58');
4156          !!!parse-error (type => 'not closed',          !!!parse-error (type => 'not closed',
4157                          value => $self->{open_elements}->[-1]->[0]                          text => $self->{open_elements}->[-1]->[0]
4158                              ->manakai_local_name,                              ->manakai_local_name,
4159                          token => $end_tag_token);                          token => $end_tag_token);
4160        }        }
# Line 3636  sub _tree_construction_main ($) { Line 4363  sub _tree_construction_main ($) {
4363    B: while (1) {    B: while (1) {
4364      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
4365        !!!cp ('t73');        !!!cp ('t73');
4366        !!!parse-error (type => 'DOCTYPE in the middle', token => $token);        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4367        ## Ignore the token        ## Ignore the token
4368        ## Stay in the phase        ## Stay in the phase
4369        !!!next-token;        !!!next-token;
# Line 3645  sub _tree_construction_main ($) { Line 4372  sub _tree_construction_main ($) {
4372               $token->{tag_name} eq 'html') {               $token->{tag_name} eq 'html') {
4373        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4374          !!!cp ('t79');          !!!cp ('t79');
4375          !!!parse-error (type => 'after html:html', token => $token);          !!!parse-error (type => 'after html', text => 'html', token => $token);
4376          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
4377        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4378          !!!cp ('t80');          !!!cp ('t80');
4379          !!!parse-error (type => 'after html:html', token => $token);          !!!parse-error (type => 'after html', text => 'html', token => $token);
4380          $self->{insertion_mode} = AFTER_FRAMESET_IM;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4381        } else {        } else {
4382          !!!cp ('t81');          !!!cp ('t81');
# Line 3690  sub _tree_construction_main ($) { Line 4417  sub _tree_construction_main ($) {
4417          !!!next-token;          !!!next-token;
4418          next B;          next B;
4419        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
4420          if ($self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL or          if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4421                 $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4422              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4423              ($token->{tag_name} eq 'svg' and              ($token->{tag_name} eq 'svg' and
4424               $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {               $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
# Line 3698  sub _tree_construction_main ($) { Line 4426  sub _tree_construction_main ($) {
4426            !!!cp ('t87.2');            !!!cp ('t87.2');
4427            #            #
4428          } elsif ({          } elsif ({
4429    ## TODO:                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4430                      center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4431                      em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4432                      h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4433                      img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4434                      nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4435                      small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4436                      sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4437                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
4438            !!!cp ('t87.2');            !!!cp ('t87.2');
4439            !!!parse-error (type => 'not closed',            !!!parse-error (type => 'not closed',
4440                            value => $self->{open_elements}->[-1]->[0]                            text => $self->{open_elements}->[-1]->[0]
4441                                ->manakai_local_name,                                ->manakai_local_name,
4442                            token => $token);                            token => $token);
4443    
4444            pop @{$self->{open_elements}}            pop @{$self->{open_elements}}
4445                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4446    
4447            $self->{insertion_mode} &= ~ $self->{insertion_mode};            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4448            ## Reprocess.            ## Reprocess.
4449            next B;            next B;
4450          } else {          } else {
4451            ## TODO: case fixup            my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4452              my $tag_name = $token->{tag_name};
4453              if ($nsuri eq $SVG_NS) {
4454                $tag_name = {
4455                   altglyph => 'altGlyph',
4456                   altglyphdef => 'altGlyphDef',
4457                   altglyphitem => 'altGlyphItem',
4458                   animatecolor => 'animateColor',
4459                   animatemotion => 'animateMotion',
4460                   animatetransform => 'animateTransform',
4461                   clippath => 'clipPath',
4462                   feblend => 'feBlend',
4463                   fecolormatrix => 'feColorMatrix',
4464                   fecomponenttransfer => 'feComponentTransfer',
4465                   fecomposite => 'feComposite',
4466                   feconvolvematrix => 'feConvolveMatrix',
4467                   fediffuselighting => 'feDiffuseLighting',
4468                   fedisplacementmap => 'feDisplacementMap',
4469                   fedistantlight => 'feDistantLight',
4470                   feflood => 'feFlood',
4471                   fefunca => 'feFuncA',
4472                   fefuncb => 'feFuncB',
4473                   fefuncg => 'feFuncG',
4474                   fefuncr => 'feFuncR',
4475                   fegaussianblur => 'feGaussianBlur',
4476                   feimage => 'feImage',
4477                   femerge => 'feMerge',
4478                   femergenode => 'feMergeNode',
4479                   femorphology => 'feMorphology',
4480                   feoffset => 'feOffset',
4481                   fepointlight => 'fePointLight',
4482                   fespecularlighting => 'feSpecularLighting',
4483                   fespotlight => 'feSpotLight',
4484                   fetile => 'feTile',
4485                   feturbulence => 'feTurbulence',
4486                   foreignobject => 'foreignObject',
4487                   glyphref => 'glyphRef',
4488                   lineargradient => 'linearGradient',
4489                   radialgradient => 'radialGradient',
4490                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4491                   textpath => 'textPath',  
4492                }->{$tag_name} || $tag_name;
4493              }
4494    
4495            !!!insert-element-f ($self->{open_elements}->[-1]->[0]->namespace_uri, $token);            ## "adjust SVG attributes" (SVG only) - done in insert-element-f
4496    
4497              ## "adjust foreign attributes" - done in insert-element-f
4498    
4499              !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
4500    
4501            if ($self->{self_closing}) {            if ($self->{self_closing}) {
4502              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 3732  sub _tree_construction_main ($) { Line 4513  sub _tree_construction_main ($) {
4513          !!!cp ('t87.5');          !!!cp ('t87.5');
4514          #          #
4515        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
         ## NOTE: "using the rules for secondary insertion mode" then "continue"  
4516          !!!cp ('t87.6');          !!!cp ('t87.6');
4517          #          !!!parse-error (type => 'not closed',
4518          ## TODO: ...                          text => $self->{open_elements}->[-1]->[0]
4519                                ->manakai_local_name,
4520                            token => $token);
4521    
4522            pop @{$self->{open_elements}}
4523                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4524    
4525            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4526            ## Reprocess.
4527            next B;
4528        } else {        } else {
4529          die "$0: $token->{type}: Unknown token type";                  die "$0: $token->{type}: Unknown token type";        
4530        }        }
# Line 3743  sub _tree_construction_main ($) { Line 4532  sub _tree_construction_main ($) {
4532    
4533      if ($self->{insertion_mode} & HEAD_IMS) {      if ($self->{insertion_mode} & HEAD_IMS) {
4534        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4535          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4536            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4537              !!!cp ('t88.2');              !!!cp ('t88.2');
4538              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4539                #
4540            } else {            } else {
4541              !!!cp ('t88.1');              !!!cp ('t88.1');
4542              ## Ignore the token.              ## Ignore the token.
4543              !!!next-token;              #
             next B;  
4544            }            }
4545            unless (length $token->{data}) {            unless (length $token->{data}) {
4546              !!!cp ('t88');              !!!cp ('t88');
4547              !!!next-token;              !!!next-token;
4548              next B;              next B;
4549            }            }
4550    ## TODO: set $token->{column} appropriately
4551          }          }
4552    
4553          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
# Line 3776  sub _tree_construction_main ($) { Line 4566  sub _tree_construction_main ($) {
4566            !!!cp ('t90');            !!!cp ('t90');
4567            ## As if </noscript>            ## As if </noscript>
4568            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4569            !!!parse-error (type => 'in noscript:#character', token => $token);            !!!parse-error (type => 'in noscript:#text', token => $token);
4570                        
4571            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4572            ## As if </head>            ## As if </head>
# Line 3812  sub _tree_construction_main ($) { Line 4602  sub _tree_construction_main ($) {
4602              !!!next-token;              !!!next-token;
4603              next B;              next B;
4604            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4605              !!!cp ('t94');              !!!cp ('t93.2');
4606              #              !!!parse-error (type => 'after head', text => 'head',
4607                                token => $token);
4608                ## Ignore the token
4609                !!!nack ('t93.3');
4610                !!!next-token;
4611                next B;
4612            } else {            } else {
4613              !!!cp ('t95');              !!!cp ('t95');
4614              !!!parse-error (type => 'in head:head', token => $token); # or in head noscript              !!!parse-error (type => 'in head:head',
4615                                token => $token); # or in head noscript
4616              ## Ignore the token              ## Ignore the token
4617              !!!nack ('t95.1');              !!!nack ('t95.1');
4618              !!!next-token;              !!!next-token;
# Line 3841  sub _tree_construction_main ($) { Line 4637  sub _tree_construction_main ($) {
4637                  !!!cp ('t98');                  !!!cp ('t98');
4638                  ## As if </noscript>                  ## As if </noscript>
4639                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4640                  !!!parse-error (type => 'in noscript:base', token => $token);                  !!!parse-error (type => 'in noscript', text => 'base',
4641                                    token => $token);
4642                                
4643                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4644                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
# Line 3852  sub _tree_construction_main ($) { Line 4649  sub _tree_construction_main ($) {
4649                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4650                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4651                  !!!cp ('t100');                  !!!cp ('t100');
4652                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4653                                    text => $token->{tag_name}, token => $token);
4654                  push @{$self->{open_elements}},                  push @{$self->{open_elements}},
4655                      [$self->{head_element}, $el_category->{head}];                      [$self->{head_element}, $el_category->{head}];
4656                } else {                } else {
4657                  !!!cp ('t101');                  !!!cp ('t101');
4658                }                }
4659                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4660                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                pop @{$self->{open_elements}};
4661                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4662                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4663                !!!nack ('t101.1');                !!!nack ('t101.1');
4664                !!!next-token;                !!!next-token;
4665                next B;                next B;
4666              } elsif ($token->{tag_name} eq 'link') {          } elsif ($token->{tag_name} eq 'link') {
4667                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4668                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4669                  !!!cp ('t102');              !!!cp ('t102');
4670                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'after head',
4671                  push @{$self->{open_elements}},                              text => $token->{tag_name}, token => $token);
4672                      [$self->{head_element}, $el_category->{head}];              push @{$self->{open_elements}},
4673                } else {                  [$self->{head_element}, $el_category->{head}];
4674                  !!!cp ('t103');            } else {
4675                }              !!!cp ('t103');
4676                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            }
4677                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4678                pop @{$self->{open_elements}} # <head>            pop @{$self->{open_elements}};
4679                    if $self->{insertion_mode} == AFTER_HEAD_IM;            pop @{$self->{open_elements}} # <head>
4680                !!!ack ('t103.1');                if $self->{insertion_mode} == AFTER_HEAD_IM;
4681                !!!next-token;            !!!ack ('t103.1');
4682                next B;            !!!next-token;
4683              next B;
4684            } elsif ($token->{tag_name} eq 'command' or
4685                     $token->{tag_name} eq 'eventsource') {
4686              if ($self->{insertion_mode} == IN_HEAD_IM) {
4687                ## NOTE: If the insertion mode at the time of the emission
4688                ## of the token was "before head", $self->{insertion_mode}
4689                ## is already changed to |IN_HEAD_IM|.
4690    
4691                ## NOTE: There is a "as if in head" code clone.
4692                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4693                pop @{$self->{open_elements}};
4694                pop @{$self->{open_elements}} # <head>
4695                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4696                !!!ack ('t103.2');
4697                !!!next-token;
4698                next B;
4699              } else {
4700                ## NOTE: "in head noscript" or "after head" insertion mode
4701                ## - in these cases, these tags are treated as same as
4702                ## normal in-body tags.
4703                !!!cp ('t103.3');
4704                #
4705              }
4706              } elsif ($token->{tag_name} eq 'meta') {              } elsif ($token->{tag_name} eq 'meta') {
4707                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4708                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4709                  !!!cp ('t104');                  !!!cp ('t104');
4710                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4711                                    text => $token->{tag_name}, token => $token);
4712                  push @{$self->{open_elements}},                  push @{$self->{open_elements}},
4713                      [$self->{head_element}, $el_category->{head}];                      [$self->{head_element}, $el_category->{head}];
4714                } else {                } else {
# Line 3896  sub _tree_construction_main ($) { Line 4718  sub _tree_construction_main ($) {
4718                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.
4719    
4720                unless ($self->{confident}) {                unless ($self->{confident}) {
4721                  if ($token->{attributes}->{charset}) { ## TODO: And if supported                  if ($token->{attributes}->{charset}) {
4722                    !!!cp ('t106');                    !!!cp ('t106');
4723                      ## NOTE: Whether the encoding is supported or not is handled
4724                      ## in the {change_encoding} callback.
4725                    $self->{change_encoding}                    $self->{change_encoding}
4726                        ->($self, $token->{attributes}->{charset}->{value},                        ->($self, $token->{attributes}->{charset}->{value},
4727                           $token);                           $token);
# Line 3907  sub _tree_construction_main ($) { Line 4731  sub _tree_construction_main ($) {
4731                                             $token->{attributes}->{charset}                                             $token->{attributes}->{charset}
4732                                                 ->{has_reference});                                                 ->{has_reference});
4733                  } elsif ($token->{attributes}->{content}) {                  } elsif ($token->{attributes}->{content}) {
                   ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
4734                    if ($token->{attributes}->{content}->{value}                    if ($token->{attributes}->{content}->{value}
4735                        =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4736                            [\x09-\x0D\x20]*=                            [\x09\x0A\x0C\x0D\x20]*=
4737                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4738                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                            ([^"'\x09\x0A\x0C\x0D\x20]
4739                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4740                      !!!cp ('t107');                      !!!cp ('t107');
4741                        ## NOTE: Whether the encoding is supported or not is handled
4742                        ## in the {change_encoding} callback.
4743                      $self->{change_encoding}                      $self->{change_encoding}
4744                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4745                             $token);                             $token);
# Line 3952  sub _tree_construction_main ($) { Line 4778  sub _tree_construction_main ($) {
4778                  !!!cp ('t111');                  !!!cp ('t111');
4779                  ## As if </noscript>                  ## As if </noscript>
4780                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4781                  !!!parse-error (type => 'in noscript:title', token => $token);                  !!!parse-error (type => 'in noscript', text => 'title',
4782                                    token => $token);
4783                                
4784                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4785                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4786                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4787                  !!!cp ('t112');                  !!!cp ('t112');
4788                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4789                                    text => $token->{tag_name}, token => $token);
4790                  push @{$self->{open_elements}},                  push @{$self->{open_elements}},
4791                      [$self->{head_element}, $el_category->{head}];                      [$self->{head_element}, $el_category->{head}];
4792                } else {                } else {
# Line 3972  sub _tree_construction_main ($) { Line 4800  sub _tree_construction_main ($) {
4800                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4801                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4802                next B;                next B;
4803              } elsif ($token->{tag_name} eq 'style') {              } elsif ($token->{tag_name} eq 'style' or
4804                         $token->{tag_name} eq 'noframes') {
4805                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4806                ## insertion mode IN_HEAD_IM)                ## insertion mode IN_HEAD_IM)
4807                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4808                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4809                  !!!cp ('t114');                  !!!cp ('t114');
4810                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4811                                    text => $token->{tag_name}, token => $token);
4812                  push @{$self->{open_elements}},                  push @{$self->{open_elements}},
4813                      [$self->{head_element}, $el_category->{head}];                      [$self->{head_element}, $el_category->{head}];
4814                } else {                } else {
# Line 3999  sub _tree_construction_main ($) { Line 4829  sub _tree_construction_main ($) {
4829                  next B;                  next B;
4830                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4831                  !!!cp ('t117');                  !!!cp ('t117');
4832                  !!!parse-error (type => 'in noscript:noscript', token => $token);                  !!!parse-error (type => 'in noscript', text => 'noscript',
4833                                    token => $token);
4834                  ## Ignore the token                  ## Ignore the token
4835                  !!!nack ('t117.1');                  !!!nack ('t117.1');
4836                  !!!next-token;                  !!!next-token;
# Line 4013  sub _tree_construction_main ($) { Line 4844  sub _tree_construction_main ($) {
4844                  !!!cp ('t119');                  !!!cp ('t119');
4845                  ## As if </noscript>                  ## As if </noscript>
4846                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4847                  !!!parse-error (type => 'in noscript:script', token => $token);                  !!!parse-error (type => 'in noscript', text => 'script',
4848                                    token => $token);
4849                                
4850                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4851                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4852                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4853                  !!!cp ('t120');                  !!!cp ('t120');
4854                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4855                                    text => $token->{tag_name}, token => $token);
4856                  push @{$self->{open_elements}},                  push @{$self->{open_elements}},
4857                      [$self->{head_element}, $el_category->{head}];                      [$self->{head_element}, $el_category->{head}];
4858                } else {                } else {
# Line 4037  sub _tree_construction_main ($) { Line 4870  sub _tree_construction_main ($) {
4870                  !!!cp ('t122');                  !!!cp ('t122');
4871                  ## As if </noscript>                  ## As if </noscript>
4872                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4873                  !!!parse-error (type => 'in noscript:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'in noscript',
4874                                    text => $token->{tag_name}, token => $token);
4875                                    
4876                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4877                  ## As if </head>                  ## As if </head>
# Line 4076  sub _tree_construction_main ($) { Line 4910  sub _tree_construction_main ($) {
4910                !!!cp ('t129');                !!!cp ('t129');
4911                ## As if </noscript>                ## As if </noscript>
4912                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4913                !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'in noscript:/',
4914                                  text => $token->{tag_name}, token => $token);
4915                                
4916                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
4917                ## As if </head>                ## As if </head>
# Line 4119  sub _tree_construction_main ($) { Line 4954  sub _tree_construction_main ($) {
4954                  !!!cp ('t133');                  !!!cp ('t133');
4955                  ## As if </noscript>                  ## As if </noscript>
4956                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4957                  !!!parse-error (type => 'in noscript:/head', token => $token);                  !!!parse-error (type => 'in noscript:/',
4958                                    text => 'head', token => $token);
4959                                    
4960                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4961                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
# Line 4132  sub _tree_construction_main ($) { Line 4968  sub _tree_construction_main ($) {
4968                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4969                  !!!next-token;                  !!!next-token;
4970                  next B;                  next B;
4971                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4972                    !!!cp ('t134.1');
4973                    !!!parse-error (type => 'unmatched end tag', text => 'head',
4974                                    token => $token);
4975                    ## Ignore the token
4976                    !!!next-token;
4977                    next B;
4978                } else {                } else {
4979                  !!!cp ('t135');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                 #  
4980                }                }
4981              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
4982                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
# Line 4143  sub _tree_construction_main ($) { Line 4985  sub _tree_construction_main ($) {
4985                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4986                  !!!next-token;                  !!!next-token;
4987                  next B;                  next B;
4988                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
4989                           $self->{insertion_mode} == AFTER_HEAD_IM) {
4990                  !!!cp ('t137');                  !!!cp ('t137');
4991                  !!!parse-error (type => 'unmatched end tag:noscript', token => $token);                  !!!parse-error (type => 'unmatched end tag',
4992                                    text => 'noscript', token => $token);
4993                  ## Ignore the token ## ISSUE: An issue in the spec.                  ## Ignore the token ## ISSUE: An issue in the spec.
4994                  !!!next-token;                  !!!next-token;
4995                  next B;                  next B;
# Line 4156  sub _tree_construction_main ($) { Line 5000  sub _tree_construction_main ($) {
5000              } elsif ({              } elsif ({
5001                        body => 1, html => 1,                        body => 1, html => 1,
5002                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5003                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5004                  !!!cp ('t139');                    $self->{insertion_mode} == IN_HEAD_IM or
5005                  ## As if <head>                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
                 !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);  
                 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});  
                 push @{$self->{open_elements}},  
                     [$self->{head_element}, $el_category->{head}];  
   
                 $self->{insertion_mode} = IN_HEAD_IM;  
                 ## Reprocess in the "in head" insertion mode...  
               } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {  
5006                  !!!cp ('t140');                  !!!cp ('t140');
5007                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5008                                    text => $token->{tag_name}, token => $token);
5009                    ## Ignore the token
5010                    !!!next-token;
5011                    next B;
5012                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5013                    !!!cp ('t140.1');
5014                    !!!parse-error (type => 'unmatched end tag',
5015                                    text => $token->{tag_name}, token => $token);
5016                  ## Ignore the token                  ## Ignore the token
5017                  !!!next-token;                  !!!next-token;
5018                  next B;                  next B;
5019                } else {                } else {
5020                  !!!cp ('t141');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
5021                }                }
5022                              } elsif ($token->{tag_name} eq 'p') {
5023                #                !!!cp ('t142');
5024              } elsif ({                !!!parse-error (type => 'unmatched end tag',
5025                        p => 1, br => 1,                                text => $token->{tag_name}, token => $token);
5026                       }->{$token->{tag_name}}) {                ## Ignore the token
5027                  !!!next-token;
5028                  next B;
5029                } elsif ($token->{tag_name} eq 'br') {
5030                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5031                  !!!cp ('t142');                  !!!cp ('t142.2');
5032                  ## As if <head>                  ## (before head) as if <head>, (in head) as if </head>
5033                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5034                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5035                  push @{$self->{open_elements}},                  $self->{insertion_mode} = AFTER_HEAD_IM;
5036                      [$self->{head_element}, $el_category->{head}];    
5037                    ## Reprocess in the "after head" insertion mode...
5038                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5039                    !!!cp ('t143.2');
5040                    ## As if </head>
5041                    pop @{$self->{open_elements}};
5042                    $self->{insertion_mode} = AFTER_HEAD_IM;
5043      
5044                    ## Reprocess in the "after head" insertion mode...
5045                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5046                    !!!cp ('t143.3');
5047                    ## ISSUE: Two parse errors for <head><noscript></br>
5048                    !!!parse-error (type => 'unmatched end tag',
5049                                    text => 'br', token => $token);
5050                    ## As if </noscript>
5051                    pop @{$self->{open_elements}};
5052                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5053    
5054                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5055                } else {                  ## As if </head>
5056                  !!!cp ('t143');                  pop @{$self->{open_elements}};
5057                }                  $self->{insertion_mode} = AFTER_HEAD_IM;
5058    
5059                #                  ## Reprocess in the "after head" insertion mode...
5060              } else {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5061                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                  !!!cp ('t143.4');
                 !!!cp ('t144');  
5062                  #                  #
5063                } else {                } else {
5064                  !!!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;  
                 next B;  
5065                }                }
5066    
5067                  ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5068                  !!!parse-error (type => 'unmatched end tag',
5069                                  text => 'br', token => $token);
5070                  ## Ignore the token
5071                  !!!next-token;
5072                  next B;
5073                } else {
5074                  !!!cp ('t145');
5075                  !!!parse-error (type => 'unmatched end tag',
5076                                  text => $token->{tag_name}, token => $token);
5077                  ## Ignore the token
5078                  !!!next-token;
5079                  next B;
5080              }              }
5081    
5082              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5083                !!!cp ('t146');                !!!cp ('t146');
5084                ## As if </noscript>                ## As if </noscript>
5085                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5086                !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'in noscript:/',
5087                                  text => $token->{tag_name}, token => $token);
5088                                
5089                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
5090                ## As if </head>                ## As if </head>
# Line 4228  sub _tree_construction_main ($) { Line 5100  sub _tree_construction_main ($) {
5100              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5101  ## ISSUE: This case cannot be reached?  ## ISSUE: This case cannot be reached?
5102                !!!cp ('t148');                !!!cp ('t148');
5103                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
5104                                  text => $token->{tag_name}, token => $token);
5105                ## Ignore the token ## ISSUE: An issue in the spec.                ## Ignore the token ## ISSUE: An issue in the spec.
5106                !!!next-token;                !!!next-token;
5107                next B;                next B;
# Line 4339  sub _tree_construction_main ($) { Line 5212  sub _tree_construction_main ($) {
5212    
5213                  !!!cp ('t153');                  !!!cp ('t153');
5214                  !!!parse-error (type => 'start tag not allowed',                  !!!parse-error (type => 'start tag not allowed',
5215                      value => $token->{tag_name}, token => $token);                      text => $token->{tag_name}, token => $token);
5216                  ## Ignore the token                  ## Ignore the token
5217                  !!!nack ('t153.1');                  !!!nack ('t153.1');
5218                  !!!next-token;                  !!!next-token;
5219                  next B;                  next B;
5220                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5221                  !!!parse-error (type => 'not closed:caption', token => $token);                  !!!parse-error (type => 'not closed', text => 'caption',
5222                                    token => $token);
5223                                    
5224                  ## NOTE: As if </caption>.                  ## NOTE: As if </caption>.
5225                  ## have a table element in table scope                  ## have a table element in table scope
# Line 4365  sub _tree_construction_main ($) { Line 5239  sub _tree_construction_main ($) {
5239    
5240                    !!!cp ('t157');                    !!!cp ('t157');
5241                    !!!parse-error (type => 'start tag not allowed',                    !!!parse-error (type => 'start tag not allowed',
5242                                    value => $token->{tag_name}, token => $token);                                    text => $token->{tag_name}, token => $token);
5243                    ## Ignore the token                    ## Ignore the token
5244                    !!!nack ('t157.1');                    !!!nack ('t157.1');
5245                    !!!next-token;                    !!!next-token;
# Line 4382  sub _tree_construction_main ($) { Line 5256  sub _tree_construction_main ($) {
5256                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5257                    !!!cp ('t159');                    !!!cp ('t159');
5258                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
5259                                    value => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
5260                                        ->manakai_local_name,                                        ->manakai_local_name,
5261                                    token => $token);                                    token => $token);
5262                  } else {                  } else {
# Line 4424  sub _tree_construction_main ($) { Line 5298  sub _tree_construction_main ($) {
5298                  } # INSCOPE                  } # INSCOPE
5299                    unless (defined $i) {                    unless (defined $i) {
5300                      !!!cp ('t165');                      !!!cp ('t165');
5301                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                      !!!parse-error (type => 'unmatched end tag',
5302                                        text => $token->{tag_name},
5303                                        token => $token);
5304                      ## Ignore the token                      ## Ignore the token
5305                      !!!next-token;                      !!!next-token;
5306                      next B;                      next B;
# Line 4441  sub _tree_construction_main ($) { Line 5317  sub _tree_construction_main ($) {
5317                          ne $token->{tag_name}) {                          ne $token->{tag_name}) {
5318                    !!!cp ('t167');                    !!!cp ('t167');
5319                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
5320                                    value => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
5321                                        ->manakai_local_name,                                        ->manakai_local_name,
5322                                    token => $token);                                    token => $token);
5323                  } else {                  } else {
# Line 4458  sub _tree_construction_main ($) { Line 5334  sub _tree_construction_main ($) {
5334                  next B;                  next B;
5335                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5336                  !!!cp ('t169');                  !!!cp ('t169');
5337                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5338                                    text => $token->{tag_name}, token => $token);
5339                  ## Ignore the token                  ## Ignore the token
5340                  !!!next-token;                  !!!next-token;
5341                  next B;                  next B;
# Line 4485  sub _tree_construction_main ($) { Line 5362  sub _tree_construction_main ($) {
5362    
5363                    !!!cp ('t173');                    !!!cp ('t173');
5364                    !!!parse-error (type => 'unmatched end tag',                    !!!parse-error (type => 'unmatched end tag',
5365                                    value => $token->{tag_name}, token => $token);                                    text => $token->{tag_name}, token => $token);
5366                    ## Ignore the token                    ## Ignore the token
5367                    !!!next-token;                    !!!next-token;
5368                    next B;                    next B;
# Line 4501  sub _tree_construction_main ($) { Line 5378  sub _tree_construction_main ($) {
5378                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5379                    !!!cp ('t175');                    !!!cp ('t175');
5380                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
5381                                    value => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
5382                                        ->manakai_local_name,                                        ->manakai_local_name,
5383                                    token => $token);                                    token => $token);
5384                  } else {                  } else {
# Line 4518  sub _tree_construction_main ($) { Line 5395  sub _tree_construction_main ($) {
5395                  next B;                  next B;
5396                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5397                  !!!cp ('t177');                  !!!cp ('t177');
5398                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5399                                    text => $token->{tag_name}, token => $token);
5400                  ## Ignore the token                  ## Ignore the token
5401                  !!!next-token;                  !!!next-token;
5402                  next B;                  next B;
# Line 4561  sub _tree_construction_main ($) { Line 5439  sub _tree_construction_main ($) {
5439    
5440                  !!!cp ('t182');                  !!!cp ('t182');
5441                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
5442                      value => $token->{tag_name}, token => $token);                      text => $token->{tag_name}, token => $token);
5443                  ## Ignore the token                  ## Ignore the token
5444                  !!!next-token;                  !!!next-token;
5445                  next B;                  next B;
5446                } # INSCOPE                } # INSCOPE
5447              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
5448                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5449                !!!parse-error (type => 'not closed:caption', token => $token);                !!!parse-error (type => 'not closed', text => 'caption',
5450                                  token => $token);
5451    
5452                ## As if </caption>                ## As if </caption>
5453                ## have a table element in table scope                ## have a table element in table scope
# Line 4586  sub _tree_construction_main ($) { Line 5465  sub _tree_construction_main ($) {
5465                } # INSCOPE                } # INSCOPE
5466                unless (defined $i) {                unless (defined $i) {
5467                  !!!cp ('t186');                  !!!cp ('t186');
5468                  !!!parse-error (type => 'unmatched end tag:caption', token => $token);                  !!!parse-error (type => 'unmatched end tag',
5469                                    text => 'caption', token => $token);
5470                  ## Ignore the token                  ## Ignore the token
5471                  !!!next-token;                  !!!next-token;
5472                  next B;                  next B;
# Line 4601  sub _tree_construction_main ($) { Line 5481  sub _tree_construction_main ($) {
5481                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5482                  !!!cp ('t188');                  !!!cp ('t188');
5483                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
5484                                  value => $self->{open_elements}->[-1]->[0]                                  text => $self->{open_elements}->[-1]->[0]
5485                                      ->manakai_local_name,                                      ->manakai_local_name,
5486                                  token => $token);                                  token => $token);
5487                } else {                } else {
# Line 4621  sub _tree_construction_main ($) { Line 5501  sub _tree_construction_main ($) {
5501                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5502                if ($self->{insertion_mode} & BODY_TABLE_IMS) {                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5503                  !!!cp ('t190');                  !!!cp ('t190');
5504                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5505                                    text => $token->{tag_name}, token => $token);
5506                  ## Ignore the token                  ## Ignore the token
5507                  !!!next-token;                  !!!next-token;
5508                  next B;                  next B;
# Line 4635  sub _tree_construction_main ($) { Line 5516  sub _tree_construction_main ($) {
5516                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5517                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5518                !!!cp ('t192');                !!!cp ('t192');
5519                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
5520                                  text => $token->{tag_name}, token => $token);
5521                ## Ignore the token                ## Ignore the token
5522                !!!next-token;                !!!next-token;
5523                next B;                next B;
# Line 4663  sub _tree_construction_main ($) { Line 5545  sub _tree_construction_main ($) {
5545      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
5546        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
5547          if (not $open_tables->[-1]->[1] and # tainted          if (not $open_tables->[-1]->[1] and # tainted
5548              $token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5549            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5550                                
5551            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 4675  sub _tree_construction_main ($) { Line 5557  sub _tree_construction_main ($) {
5557            }            }
5558          }          }
5559    
5560              !!!parse-error (type => 'in table:#character', token => $token);          !!!parse-error (type => 'in table:#text', token => $token);
5561    
5562              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
5563              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
# Line 4726  sub _tree_construction_main ($) { Line 5608  sub _tree_construction_main ($) {
5608          !!!next-token;          !!!next-token;
5609          next B;          next B;
5610        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5611              if ({          if ({
5612                   tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => ($self->{insertion_mode} != IN_ROW_IM),
5613                   th => 1, td => 1,               th => 1, td => 1,
5614                  }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
5615                if ($self->{insertion_mode} == IN_TABLE_IM) {            if ($self->{insertion_mode} == IN_TABLE_IM) {
5616                  ## Clear back to table context              ## Clear back to table context
5617                  while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
5618                                  & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
5619                    !!!cp ('t201');                !!!cp ('t201');
5620                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5621                  }              }
5622                                
5623                  !!!insert-element ('tbody',, $token);              !!!insert-element ('tbody',, $token);
5624                  $self->{insertion_mode} = IN_TABLE_BODY_IM;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5625                  ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
5626                }            }
5627              
5628                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5629                  unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
5630                    !!!cp ('t202');                !!!cp ('t202');
5631                    !!!parse-error (type => 'missing start tag:tr', token => $token);                !!!parse-error (type => 'missing start tag:tr', token => $token);
5632                  }              }
5633                                    
5634                  ## Clear back to table body context              ## Clear back to table body context
5635                  while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
5636                                  & TABLE_ROWS_SCOPING_EL)) {                              & TABLE_ROWS_SCOPING_EL)) {
5637                    !!!cp ('t203');                !!!cp ('t203');
5638                    ## ISSUE: Can this case be reached?                ## ISSUE: Can this case be reached?
5639                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5640                  }              }
5641                                    
5642                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5643                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
# Line 4811  sub _tree_construction_main ($) { Line 5693  sub _tree_construction_main ($) {
5693                  unless (defined $i) {                  unless (defined $i) {
5694                    !!!cp ('t210');                    !!!cp ('t210');
5695  ## TODO: This type is wrong.  ## TODO: This type is wrong.
5696                    !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name}, token => $token);                    !!!parse-error (type => 'unmacthed end tag',
5697                                      text => $token->{tag_name}, token => $token);
5698                    ## Ignore the token                    ## Ignore the token
5699                    !!!nack ('t210.1');                    !!!nack ('t210.1');
5700                    !!!next-token;                    !!!next-token;
# Line 4855  sub _tree_construction_main ($) { Line 5738  sub _tree_construction_main ($) {
5738                  } # INSCOPE                  } # INSCOPE
5739                  unless (defined $i) {                  unless (defined $i) {
5740                    !!!cp ('t216');                    !!!cp ('t216');
5741  ## TODO: This erorr type ios wrong.  ## TODO: This erorr type is wrong.
5742                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                    !!!parse-error (type => 'unmatched end tag',
5743                                      text => $token->{tag_name}, token => $token);
5744                    ## Ignore the token                    ## Ignore the token
5745                    !!!nack ('t216.1');                    !!!nack ('t216.1');
5746                    !!!next-token;                    !!!next-token;
# Line 4931  sub _tree_construction_main ($) { Line 5815  sub _tree_construction_main ($) {
5815                }                }
5816              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5817                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
5818                                value => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
5819                                    ->manakai_local_name,                                    ->manakai_local_name,
5820                                token => $token);                                token => $token);
5821    
# Line 4952  sub _tree_construction_main ($) { Line 5836  sub _tree_construction_main ($) {
5836                unless (defined $i) {                unless (defined $i) {
5837                  !!!cp ('t223');                  !!!cp ('t223');
5838  ## TODO: The following is wrong, maybe.  ## TODO: The following is wrong, maybe.
5839                  !!!parse-error (type => 'unmatched end tag:table', token => $token);                  !!!parse-error (type => 'unmatched end tag', text => 'table',
5840                                    token => $token);
5841                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5842                  !!!nack ('t223.1');                  !!!nack ('t223.1');
5843                  !!!next-token;                  !!!next-token;
5844                  next B;                  next B;
5845                }                }
5846                                
5847  ## TODO: Followings are removed from the latest spec.  ## TODO: Followings are removed from the latest spec.
5848                ## generate implied end tags                ## generate implied end tags
5849                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5850                  !!!cp ('t224');                  !!!cp ('t224');
# Line 4970  sub _tree_construction_main ($) { Line 5855  sub _tree_construction_main ($) {
5855                  !!!cp ('t225');                  !!!cp ('t225');
5856                  ## NOTE: |<table><tr><table>|                  ## NOTE: |<table><tr><table>|
5857                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
5858                                  value => $self->{open_elements}->[-1]->[0]                                  text => $self->{open_elements}->[-1]->[0]
5859                                      ->manakai_local_name,                                      ->manakai_local_name,
5860                                  token => $token);                                  token => $token);
5861                } else {                } else {
# Line 5011  sub _tree_construction_main ($) { Line 5896  sub _tree_construction_main ($) {
5896                my $type = lc $token->{attributes}->{type}->{value};                my $type = lc $token->{attributes}->{type}->{value};
5897                if ($type eq 'hidden') {                if ($type eq 'hidden') {
5898                  !!!cp ('t227.3');                  !!!cp ('t227.3');
5899                  !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'in table',
5900                                    text => $token->{tag_name}, token => $token);
5901    
5902                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5903    
# Line 5039  sub _tree_construction_main ($) { Line 5925  sub _tree_construction_main ($) {
5925            #            #
5926          }          }
5927    
5928          !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);          !!!parse-error (type => 'in table', text => $token->{tag_name},
5929                            token => $token);
5930    
5931          $insert = $insert_to_foster;          $insert = $insert_to_foster;
5932          #          #
# Line 5061  sub _tree_construction_main ($) { Line 5948  sub _tree_construction_main ($) {
5948                } # INSCOPE                } # INSCOPE
5949                unless (defined $i) {                unless (defined $i) {
5950                  !!!cp ('t230');                  !!!cp ('t230');
5951                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5952                                    text => $token->{tag_name}, token => $token);
5953                  ## Ignore the token                  ## Ignore the token
5954                  !!!nack ('t230.1');                  !!!nack ('t230.1');
5955                  !!!next-token;                  !!!next-token;
# Line 5102  sub _tree_construction_main ($) { Line 5990  sub _tree_construction_main ($) {
5990                  unless (defined $i) {                  unless (defined $i) {
5991                    !!!cp ('t235');                    !!!cp ('t235');
5992  ## TODO: The following is wrong.  ## TODO: The following is wrong.
5993                    !!!parse-error (type => 'unmatched end tag:'.$token->{type}, token => $token);                    !!!parse-error (type => 'unmatched end tag',
5994                                      text => $token->{type}, token => $token);
5995                    ## Ignore the token                    ## Ignore the token
5996                    !!!nack ('t236.1');                    !!!nack ('t236.1');
5997                    !!!next-token;                    !!!next-token;
# Line 5138  sub _tree_construction_main ($) { Line 6027  sub _tree_construction_main ($) {
6027                  } # INSCOPE                  } # INSCOPE
6028                  unless (defined $i) {                  unless (defined $i) {
6029                    !!!cp ('t239');                    !!!cp ('t239');
6030                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                    !!!parse-error (type => 'unmatched end tag',
6031                                      text => $token->{tag_name}, token => $token);
6032                    ## Ignore the token                    ## Ignore the token
6033                    !!!nack ('t239.1');                    !!!nack ('t239.1');
6034                    !!!next-token;                    !!!next-token;
# Line 5184  sub _tree_construction_main ($) { Line 6074  sub _tree_construction_main ($) {
6074                } # INSCOPE                } # INSCOPE
6075                unless (defined $i) {                unless (defined $i) {
6076                  !!!cp ('t243');                  !!!cp ('t243');
6077                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
6078                                    text => $token->{tag_name}, token => $token);
6079                  ## Ignore the token                  ## Ignore the token
6080                  !!!nack ('t243.1');                  !!!nack ('t243.1');
6081                  !!!next-token;                  !!!next-token;
# Line 5218  sub _tree_construction_main ($) { Line 6109  sub _tree_construction_main ($) {
6109                  } # INSCOPE                  } # INSCOPE
6110                    unless (defined $i) {                    unless (defined $i) {
6111                      !!!cp ('t249');                      !!!cp ('t249');
6112                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                      !!!parse-error (type => 'unmatched end tag',
6113                                        text => $token->{tag_name}, token => $token);
6114                      ## Ignore the token                      ## Ignore the token
6115                      !!!nack ('t249.1');                      !!!nack ('t249.1');
6116                      !!!next-token;                      !!!next-token;
# Line 5241  sub _tree_construction_main ($) { Line 6133  sub _tree_construction_main ($) {
6133                  } # INSCOPE                  } # INSCOPE
6134                    unless (defined $i) {                    unless (defined $i) {
6135                      !!!cp ('t252');                      !!!cp ('t252');
6136                      !!!parse-error (type => 'unmatched end tag:tr', token => $token);                      !!!parse-error (type => 'unmatched end tag',
6137                                        text => 'tr', token => $token);
6138                      ## Ignore the token                      ## Ignore the token
6139                      !!!nack ('t252.1');                      !!!nack ('t252.1');
6140                      !!!next-token;                      !!!next-token;
# Line 5276  sub _tree_construction_main ($) { Line 6169  sub _tree_construction_main ($) {
6169                } # INSCOPE                } # INSCOPE
6170                unless (defined $i) {                unless (defined $i) {
6171                  !!!cp ('t256');                  !!!cp ('t256');
6172                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
6173                                    text => $token->{tag_name}, token => $token);
6174                  ## Ignore the token                  ## Ignore the token
6175                  !!!nack ('t256.1');                  !!!nack ('t256.1');
6176                  !!!next-token;                  !!!next-token;
# Line 5303  sub _tree_construction_main ($) { Line 6197  sub _tree_construction_main ($) {
6197                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6198                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6199            !!!cp ('t258');            !!!cp ('t258');
6200            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
6201                              text => $token->{tag_name}, token => $token);
6202            ## Ignore the token            ## Ignore the token
6203            !!!nack ('t258.1');            !!!nack ('t258.1');
6204             !!!next-token;             !!!next-token;
6205            next B;            next B;
6206          } else {          } else {
6207            !!!cp ('t259');            !!!cp ('t259');
6208            !!!parse-error (type => 'in table:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'in table:/',
6209                              text => $token->{tag_name}, token => $token);
6210    
6211            $insert = $insert_to_foster;            $insert = $insert_to_foster;
6212            #            #
# Line 5333  sub _tree_construction_main ($) { Line 6229  sub _tree_construction_main ($) {
6229        }        }
6230      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6231            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6232              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6233                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6234                unless (length $token->{data}) {                unless (length $token->{data}) {
6235                  !!!cp ('t260');                  !!!cp ('t260');
# Line 5360  sub _tree_construction_main ($) { Line 6256  sub _tree_construction_main ($) {
6256              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
6257                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6258                  !!!cp ('t264');                  !!!cp ('t264');
6259                  !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);                  !!!parse-error (type => 'unmatched end tag',
6260                                    text => 'colgroup', token => $token);
6261                  ## Ignore the token                  ## Ignore the token
6262                  !!!next-token;                  !!!next-token;
6263                  next B;                  next B;
# Line 5373  sub _tree_construction_main ($) { Line 6270  sub _tree_construction_main ($) {
6270                }                }
6271              } elsif ($token->{tag_name} eq 'col') {              } elsif ($token->{tag_name} eq 'col') {
6272                !!!cp ('t266');                !!!cp ('t266');
6273                !!!parse-error (type => 'unmatched end tag:col', token => $token);                !!!parse-error (type => 'unmatched end tag',
6274                                  text => 'col', token => $token);
6275                ## Ignore the token                ## Ignore the token
6276                !!!next-token;                !!!next-token;
6277                next B;                next B;
# Line 5403  sub _tree_construction_main ($) { Line 6301  sub _tree_construction_main ($) {
6301            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6302              !!!cp ('t269');              !!!cp ('t269');
6303  ## TODO: Wrong error type?  ## TODO: Wrong error type?
6304              !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);              !!!parse-error (type => 'unmatched end tag',
6305                                text => 'colgroup', token => $token);
6306              ## Ignore the token              ## Ignore the token
6307              !!!nack ('t269.1');              !!!nack ('t269.1');
6308              !!!next-token;              !!!next-token;
# Line 5457  sub _tree_construction_main ($) { Line 6356  sub _tree_construction_main ($) {
6356            !!!nack ('t277.1');            !!!nack ('t277.1');
6357            !!!next-token;            !!!next-token;
6358            next B;            next B;
6359          } elsif ($token->{tag_name} eq 'select' or          } elsif ({
6360                   $token->{tag_name} eq 'input' or                     select => 1, input => 1, textarea => 1,
6361                     }->{$token->{tag_name}} or
6362                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6363                    {                    {
6364                     caption => 1, table => 1,                     caption => 1, table => 1,
# Line 5466  sub _tree_construction_main ($) { Line 6366  sub _tree_construction_main ($) {
6366                     tr => 1, td => 1, th => 1,                     tr => 1, td => 1, th => 1,
6367                    }->{$token->{tag_name}})) {                    }->{$token->{tag_name}})) {
6368            ## TODO: The type below is not good - <select> is replaced by </select>            ## TODO: The type below is not good - <select> is replaced by </select>
6369            !!!parse-error (type => 'not closed:select', token => $token);            !!!parse-error (type => 'not closed', text => 'select',
6370                              token => $token);
6371            ## NOTE: As if the token were </select> (<select> case) or            ## NOTE: As if the token were </select> (<select> case) or
6372            ## as if there were </select> (otherwise).            ## as if there were </select> (otherwise).
6373            ## have an element in table scope            ## have an element in table scope
# Line 5484  sub _tree_construction_main ($) { Line 6385  sub _tree_construction_main ($) {
6385            } # INSCOPE            } # INSCOPE
6386            unless (defined $i) {            unless (defined $i) {
6387              !!!cp ('t280');              !!!cp ('t280');
6388              !!!parse-error (type => 'unmatched end tag:select', token => $token);              !!!parse-error (type => 'unmatched end tag',
6389                                text => 'select', token => $token);
6390              ## Ignore the token              ## Ignore the token
6391              !!!nack ('t280.1');              !!!nack ('t280.1');
6392              !!!next-token;              !!!next-token;
# Line 5508  sub _tree_construction_main ($) { Line 6410  sub _tree_construction_main ($) {
6410            }            }
6411          } else {          } else {
6412            !!!cp ('t282');            !!!cp ('t282');
6413            !!!parse-error (type => 'in select:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'in select',
6414                              text => $token->{tag_name}, token => $token);
6415            ## Ignore the token            ## Ignore the token
6416            !!!nack ('t282.1');            !!!nack ('t282.1');
6417            !!!next-token;            !!!next-token;
# Line 5526  sub _tree_construction_main ($) { Line 6429  sub _tree_construction_main ($) {
6429              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6430            } else {            } else {
6431              !!!cp ('t285');              !!!cp ('t285');
6432              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
6433                                text => $token->{tag_name}, token => $token);
6434              ## Ignore the token              ## Ignore the token
6435            }            }
6436            !!!nack ('t285.1');            !!!nack ('t285.1');
# Line 5538  sub _tree_construction_main ($) { Line 6442  sub _tree_construction_main ($) {
6442              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6443            } else {            } else {
6444              !!!cp ('t287');              !!!cp ('t287');
6445              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
6446                                text => $token->{tag_name}, token => $token);
6447              ## Ignore the token              ## Ignore the token
6448            }            }
6449            !!!nack ('t287.1');            !!!nack ('t287.1');
# Line 5560  sub _tree_construction_main ($) { Line 6465  sub _tree_construction_main ($) {
6465            } # INSCOPE            } # INSCOPE
6466            unless (defined $i) {            unless (defined $i) {
6467              !!!cp ('t290');              !!!cp ('t290');
6468              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
6469                                text => $token->{tag_name}, token => $token);
6470              ## Ignore the token              ## Ignore the token
6471              !!!nack ('t290.1');              !!!nack ('t290.1');
6472              !!!next-token;              !!!next-token;
# Line 5581  sub _tree_construction_main ($) { Line 6487  sub _tree_construction_main ($) {
6487                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6488                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
6489  ## TODO: The following is wrong?  ## TODO: The following is wrong?
6490            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
6491                              text => $token->{tag_name}, token => $token);
6492                                
6493            ## have an element in table scope            ## have an element in table scope
6494            my $i;            my $i;
# Line 5622  sub _tree_construction_main ($) { Line 6529  sub _tree_construction_main ($) {
6529            unless (defined $i) {            unless (defined $i) {
6530              !!!cp ('t297');              !!!cp ('t297');
6531  ## TODO: The following error type is correct?  ## TODO: The following error type is correct?
6532              !!!parse-error (type => 'unmatched end tag:select', token => $token);              !!!parse-error (type => 'unmatched end tag',
6533                                text => 'select', token => $token);
6534              ## Ignore the </select> token              ## Ignore the </select> token
6535              !!!nack ('t297.1');              !!!nack ('t297.1');
6536              !!!next-token; ## TODO: ok?              !!!next-token; ## TODO: ok?
# Line 5639  sub _tree_construction_main ($) { Line 6547  sub _tree_construction_main ($) {
6547            next B;            next B;
6548          } else {          } else {
6549            !!!cp ('t299');            !!!cp ('t299');
6550            !!!parse-error (type => 'in select:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'in select:/',
6551                              text => $token->{tag_name}, token => $token);
6552            ## Ignore the token            ## Ignore the token
6553            !!!nack ('t299.3');            !!!nack ('t299.3');
6554            !!!next-token;            !!!next-token;
# Line 5661  sub _tree_construction_main ($) { Line 6570  sub _tree_construction_main ($) {
6570        }        }
6571      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6572        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6573          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6574            my $data = $1;            my $data = $1;
6575            ## As if in body            ## As if in body
6576            $reconstruct_active_formatting_elements->($insert_to_current);            $reconstruct_active_formatting_elements->($insert_to_current);
# Line 5677  sub _tree_construction_main ($) { Line 6586  sub _tree_construction_main ($) {
6586                    
6587          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6588            !!!cp ('t301');            !!!cp ('t301');
6589            !!!parse-error (type => 'after html:#character', token => $token);            !!!parse-error (type => 'after html:#text', token => $token);
6590              #
           ## Reprocess in the "after body" insertion mode.  
6591          } else {          } else {
6592            !!!cp ('t302');            !!!cp ('t302');
6593              ## "after body" insertion mode
6594              !!!parse-error (type => 'after body:#text', token => $token);
6595              #
6596          }          }
           
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:#character', token => $token);  
6597    
6598          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6599          ## reprocess          ## reprocess
# Line 5693  sub _tree_construction_main ($) { Line 6601  sub _tree_construction_main ($) {
6601        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6602          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6603            !!!cp ('t303');            !!!cp ('t303');
6604            !!!parse-error (type => 'after html:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'after html',
6605                                        text => $token->{tag_name}, token => $token);
6606            ## Reprocess in the "after body" insertion mode.            #
6607          } else {          } else {
6608            !!!cp ('t304');            !!!cp ('t304');
6609              ## "after body" insertion mode
6610              !!!parse-error (type => 'after body',
6611                              text => $token->{tag_name}, token => $token);
6612              #
6613          }          }
6614    
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:'.$token->{tag_name}, token => $token);  
   
6615          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6616          !!!ack-later;          !!!ack-later;
6617          ## reprocess          ## reprocess
# Line 5710  sub _tree_construction_main ($) { Line 6619  sub _tree_construction_main ($) {
6619        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6620          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6621            !!!cp ('t305');            !!!cp ('t305');
6622            !!!parse-error (type => 'after html:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'after html:/',
6623                              text => $token->{tag_name}, token => $token);
6624                        
6625            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6626            ## Reprocess in the "after body" insertion mode.            ## Reprocess.
6627              next B;
6628          } else {          } else {
6629            !!!cp ('t306');            !!!cp ('t306');
6630          }          }
# Line 5722  sub _tree_construction_main ($) { Line 6633  sub _tree_construction_main ($) {
6633          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
6634            if (defined $self->{inner_html_node}) {            if (defined $self->{inner_html_node}) {
6635              !!!cp ('t307');              !!!cp ('t307');
6636              !!!parse-error (type => 'unmatched end tag:html', token => $token);              !!!parse-error (type => 'unmatched end tag',
6637                                text => 'html', token => $token);
6638              ## Ignore the token              ## Ignore the token
6639              !!!next-token;              !!!next-token;
6640              next B;              next B;
# Line 5734  sub _tree_construction_main ($) { Line 6646  sub _tree_construction_main ($) {
6646            }            }
6647          } else {          } else {
6648            !!!cp ('t309');            !!!cp ('t309');
6649            !!!parse-error (type => 'after body:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'after body:/',
6650                              text => $token->{tag_name}, token => $token);
6651    
6652            $self->{insertion_mode} = IN_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6653            ## reprocess            ## reprocess
# Line 5749  sub _tree_construction_main ($) { Line 6662  sub _tree_construction_main ($) {
6662        }        }
6663      } elsif ($self->{insertion_mode} & FRAME_IMS) {      } elsif ($self->{insertion_mode} & FRAME_IMS) {
6664        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6665          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6666            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6667                        
6668            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 5759  sub _tree_construction_main ($) { Line 6672  sub _tree_construction_main ($) {
6672            }            }
6673          }          }
6674                    
6675          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6676            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6677              !!!cp ('t311');              !!!cp ('t311');
6678              !!!parse-error (type => 'in frameset:#character', token => $token);              !!!parse-error (type => 'in frameset:#text', token => $token);
6679            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6680              !!!cp ('t312');              !!!cp ('t312');
6681              !!!parse-error (type => 'after frameset:#character', token => $token);              !!!parse-error (type => 'after frameset:#text', token => $token);
6682            } else { # "after html frameset"            } else { # "after after frameset"
6683              !!!cp ('t313');              !!!cp ('t313');
6684              !!!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);  
6685            }            }
6686                        
6687            ## Ignore the token.            ## Ignore the token.
# Line 5788  sub _tree_construction_main ($) { Line 6697  sub _tree_construction_main ($) {
6697                    
6698          die qq[$0: Character "$token->{data}"];          die qq[$0: Character "$token->{data}"];
6699        } 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');  
         }  
   
6700          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6701              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6702            !!!cp ('t318');            !!!cp ('t318');
# Line 5815  sub _tree_construction_main ($) { Line 6714  sub _tree_construction_main ($) {
6714            next B;            next B;
6715          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
6716            !!!cp ('t320');            !!!cp ('t320');
6717            ## NOTE: As if in body.            ## NOTE: As if in head.
6718            $parse_rcdata->(CDATA_CONTENT_MODEL);            $parse_rcdata->(CDATA_CONTENT_MODEL);
6719            next B;            next B;
6720    
6721              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6722              ## has no parse error.
6723          } else {          } else {
6724            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6725              !!!cp ('t321');              !!!cp ('t321');
6726              !!!parse-error (type => 'in frameset:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'in frameset',
6727            } else {                              text => $token->{tag_name}, token => $token);
6728              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6729              !!!cp ('t322');              !!!cp ('t322');
6730              !!!parse-error (type => 'after frameset:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'after frameset',
6731                                text => $token->{tag_name}, token => $token);
6732              } else { # "after after frameset"
6733                !!!cp ('t322.2');
6734                !!!parse-error (type => 'after after frameset',
6735                                text => $token->{tag_name}, token => $token);
6736            }            }
6737            ## Ignore the token            ## Ignore the token
6738            !!!nack ('t322.1');            !!!nack ('t322.1');
# Line 5832  sub _tree_construction_main ($) { Line 6740  sub _tree_construction_main ($) {
6740            next B;            next B;
6741          }          }
6742        } 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');  
         }  
   
6743          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6744              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6745            if ($self->{open_elements}->[-1]->[1] & HTML_EL and            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6746                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
6747              !!!cp ('t325');              !!!cp ('t325');
6748              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
6749                                text => $token->{tag_name}, token => $token);
6750              ## Ignore the token              ## Ignore the token
6751              !!!next-token;              !!!next-token;
6752            } else {            } else {
# Line 5873  sub _tree_construction_main ($) { Line 6772  sub _tree_construction_main ($) {
6772          } else {          } else {
6773            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6774              !!!cp ('t330');              !!!cp ('t330');
6775              !!!parse-error (type => 'in frameset:/'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'in frameset:/',
6776            } else {                              text => $token->{tag_name}, token => $token);
6777              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6778                !!!cp ('t330.1');
6779                !!!parse-error (type => 'after frameset:/',
6780                                text => $token->{tag_name}, token => $token);
6781              } else { # "after after html"
6782              !!!cp ('t331');              !!!cp ('t331');
6783              !!!parse-error (type => 'after frameset:/'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'after after frameset:/',
6784                                text => $token->{tag_name}, token => $token);
6785            }            }
6786            ## Ignore the token            ## Ignore the token
6787            !!!next-token;            !!!next-token;
# Line 5915  sub _tree_construction_main ($) { Line 6820  sub _tree_construction_main ($) {
6820          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
6821          next B;          next B;
6822        } elsif ({        } elsif ({
6823                  base => 1, link => 1,                  base => 1, command => 1, eventsource => 1, link => 1,
6824                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6825          !!!cp ('t334');          !!!cp ('t334');
6826          ## 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
6827          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6828          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}};
6829          !!!ack ('t334.1');          !!!ack ('t334.1');
6830          !!!next-token;          !!!next-token;
6831          next B;          next B;
6832        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
6833          ## 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
6834          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6835          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          my $meta_el = pop @{$self->{open_elements}};
6836    
6837          unless ($self->{confident}) {          unless ($self->{confident}) {
6838            if ($token->{attributes}->{charset}) { ## TODO: And if supported            if ($token->{attributes}->{charset}) {
6839              !!!cp ('t335');              !!!cp ('t335');
6840                ## NOTE: Whether the encoding is supported or not is handled
6841                ## in the {change_encoding} callback.
6842              $self->{change_encoding}              $self->{change_encoding}
6843                  ->($self, $token->{attributes}->{charset}->{value}, $token);                  ->($self, $token->{attributes}->{charset}->{value}, $token);
6844                            
# Line 5940  sub _tree_construction_main ($) { Line 6847  sub _tree_construction_main ($) {
6847                                       $token->{attributes}->{charset}                                       $token->{attributes}->{charset}
6848                                           ->{has_reference});                                           ->{has_reference});
6849            } elsif ($token->{attributes}->{content}) {            } elsif ($token->{attributes}->{content}) {
             ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
6850              if ($token->{attributes}->{content}->{value}              if ($token->{attributes}->{content}->{value}
6851                  =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6852                      [\x09-\x0D\x20]*=                      [\x09\x0A\x0C\x0D\x20]*=
6853                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                      [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6854                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                      ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6855                       /x) {
6856                !!!cp ('t336');                !!!cp ('t336');
6857                  ## NOTE: Whether the encoding is supported or not is handled
6858                  ## in the {change_encoding} callback.
6859                $self->{change_encoding}                $self->{change_encoding}
6860                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6861                $meta_el->[0]->get_attribute_node_ns (undef, 'content')                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
# Line 5981  sub _tree_construction_main ($) { Line 6890  sub _tree_construction_main ($) {
6890          $parse_rcdata->(RCDATA_CONTENT_MODEL);          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6891          next B;          next B;
6892        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
6893          !!!parse-error (type => 'in body:body', token => $token);          !!!parse-error (type => 'in body', text => 'body', token => $token);
6894                                
6895          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
6896              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
# Line 6088  sub _tree_construction_main ($) { Line 6997  sub _tree_construction_main ($) {
6997              last INSCOPE;              last INSCOPE;
6998            }            }
6999          } # INSCOPE          } # INSCOPE
7000    
7001            ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
7002              ## Interpreted as <li><foo/></li><li/> (non-conforming)
7003              ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
7004              ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
7005              ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
7006              ## object (Fx)
7007              ## Generate non-tree (non-conforming)
7008              ## basefont (IE7 (where basefont is non-void)), center (IE),
7009              ## form (IE), hn (IE)
7010            ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7011              ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7012              ## div (Fx, S)
7013                        
7014          ## Step 1          ## Step 1
7015          my $i = -1;          my $i = -1;
# Line 6101  sub _tree_construction_main ($) { Line 7023  sub _tree_construction_main ($) {
7023              if ($i != -1) {              if ($i != -1) {
7024                !!!cp ('t355');                !!!cp ('t355');
7025                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
7026                                value => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
7027                                    ->manakai_local_name,                                    ->manakai_local_name,
7028                                token => $token);                                token => $token);
7029              } else {              } else {
# Line 6255  sub _tree_construction_main ($) { Line 7177  sub _tree_construction_main ($) {
7177                  xmp => 1,                  xmp => 1,
7178                  iframe => 1,                  iframe => 1,
7179                  noembed => 1,                  noembed => 1,
7180                  noframes => 1,                  noframes => 1, ## NOTE: This is an "as if in head" code clone.
7181                  noscript => 0, ## TODO: 1 if scripting is enabled                  noscript => 0, ## TODO: 1 if scripting is enabled
7182                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7183          if ($token->{tag_name} eq 'xmp') {          if ($token->{tag_name} eq 'xmp') {
# Line 6277  sub _tree_construction_main ($) { Line 7199  sub _tree_construction_main ($) {
7199            !!!next-token;            !!!next-token;
7200            next B;            next B;
7201          } else {          } else {
7202              !!!ack ('t391.1');
7203    
7204            my $at = $token->{attributes};            my $at = $token->{attributes};
7205            my $form_attrs;            my $form_attrs;
7206            $form_attrs->{action} = $at->{action} if $at->{action};            $form_attrs->{action} = $at->{action} if $at->{action};
# Line 6320  sub _tree_construction_main ($) { Line 7244  sub _tree_construction_main ($) {
7244                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
7245                          {type => END_TAG_TOKEN, tag_name => 'form',                          {type => END_TAG_TOKEN, tag_name => 'form',
7246                           line => $token->{line}, column => $token->{column}};                           line => $token->{line}, column => $token->{column}};
           !!!nack ('t391.1'); ## NOTE: Not acknowledged.  
7247            !!!back-token (@tokens);            !!!back-token (@tokens);
7248            !!!next-token;            !!!next-token;
7249            next B;            next B;
# Line 6368  sub _tree_construction_main ($) { Line 7291  sub _tree_construction_main ($) {
7291            ## Ignore the token            ## Ignore the token
7292          } else {          } else {
7293            !!!cp ('t398');            !!!cp ('t398');
7294            !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token);            !!!parse-error (type => 'in RCDATA:#eof', token => $token);
7295          }          }
7296          !!!next-token;          !!!next-token;
7297          next B;          next B;
7298          } elsif ($token->{tag_name} eq 'rt' or
7299                   $token->{tag_name} eq 'rp') {
7300            ## has a |ruby| element in scope
7301            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7302              my $node = $self->{open_elements}->[$_];
7303              if ($node->[1] & RUBY_EL) {
7304                !!!cp ('t398.1');
7305                ## generate implied end tags
7306                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7307                  !!!cp ('t398.2');
7308                  pop @{$self->{open_elements}};
7309                }
7310                unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7311                  !!!cp ('t398.3');
7312                  !!!parse-error (type => 'not closed',
7313                                  text => $self->{open_elements}->[-1]->[0]
7314                                      ->manakai_local_name,
7315                                  token => $token);
7316                  pop @{$self->{open_elements}}
7317                      while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
7318                }
7319                last INSCOPE;
7320              } elsif ($node->[1] & SCOPING_EL) {
7321                !!!cp ('t398.4');
7322                last INSCOPE;
7323              }
7324            } # INSCOPE
7325    
7326            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7327    
7328            !!!nack ('t398.5');
7329            !!!next-token;
7330            redo B;
7331        } elsif ($token->{tag_name} eq 'math' or        } elsif ($token->{tag_name} eq 'math' or
7332                 $token->{tag_name} eq 'svg') {                 $token->{tag_name} eq 'svg') {
7333          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7334    
7335            ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7336    
7337            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7338    
7339            ## "adjust foreign attributes" - done in insert-element-f
7340                    
7341          !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token);          !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7342                    
7343          if ($self->{self_closing}) {          if ($self->{self_closing}) {
7344            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
# Line 6398  sub _tree_construction_main ($) { Line 7360  sub _tree_construction_main ($) {
7360                  thead => 1, tr => 1,                  thead => 1, tr => 1,
7361                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7362          !!!cp ('t401');          !!!cp ('t401');
7363          !!!parse-error (type => 'in body:'.$token->{tag_name}, token => $token);          !!!parse-error (type => 'in body',
7364                            text => $token->{tag_name}, token => $token);
7365          ## Ignore the token          ## Ignore the token
7366          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7367          !!!next-token;          !!!next-token;
# Line 6427  sub _tree_construction_main ($) { Line 7390  sub _tree_construction_main ($) {
7390            !!!nack ('t380.1');            !!!nack ('t380.1');
7391          } elsif ({          } elsif ({
7392                    b => 1, big => 1, em => 1, font => 1, i => 1,                    b => 1, big => 1, em => 1, font => 1, i => 1,
7393                    s => 1, small => 1, strile => 1,                    s => 1, small => 1, strike => 1,
7394                    strong => 1, tt => 1, u => 1,                    strong => 1, tt => 1, u => 1,
7395                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
7396            !!!cp ('t375');            !!!cp ('t375');
# Line 6483  sub _tree_construction_main ($) { Line 7446  sub _tree_construction_main ($) {
7446            }            }
7447    
7448            !!!parse-error (type => 'start tag not allowed',            !!!parse-error (type => 'start tag not allowed',
7449                            value => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
7450            ## NOTE: Ignore the token.            ## NOTE: Ignore the token.
7451            !!!next-token;            !!!next-token;
7452            next B;            next B;
# Line 6493  sub _tree_construction_main ($) { Line 7456  sub _tree_construction_main ($) {
7456            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7457              !!!cp ('t403');              !!!cp ('t403');
7458              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
7459                              value => $_->[0]->manakai_local_name,                              text => $_->[0]->manakai_local_name,
7460                              token => $token);                              token => $token);
7461              last;              last;
7462            } else {            } else {
# Line 6513  sub _tree_construction_main ($) { Line 7476  sub _tree_construction_main ($) {
7476            unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {            unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7477              !!!cp ('t406');              !!!cp ('t406');
7478              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
7479                              value => $self->{open_elements}->[1]->[0]                              text => $self->{open_elements}->[1]->[0]
7480                                  ->manakai_local_name,                                  ->manakai_local_name,
7481                              token => $token);                              token => $token);
7482            } else {            } else {
# Line 6524  sub _tree_construction_main ($) { Line 7487  sub _tree_construction_main ($) {
7487            next B;            next B;
7488          } else {          } else {
7489            !!!cp ('t408');            !!!cp ('t408');
7490            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7491                              text => $token->{tag_name}, token => $token);
7492            ## Ignore the token            ## Ignore the token
7493            !!!next-token;            !!!next-token;
7494            next B;            next B;
# Line 6552  sub _tree_construction_main ($) { Line 7516  sub _tree_construction_main ($) {
7516    
7517          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7518            !!!cp ('t413');            !!!cp ('t413');
7519            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7520                              text => $token->{tag_name}, token => $token);
7521              ## NOTE: Ignore the token.
7522          } else {          } else {
7523            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7524            while ({            while ({
7525                      ## END_TAG_OPTIONAL_EL
7526                    dd => ($token->{tag_name} ne 'dd'),                    dd => ($token->{tag_name} ne 'dd'),
7527                    dt => ($token->{tag_name} ne 'dt'),                    dt => ($token->{tag_name} ne 'dt'),
7528                    li => ($token->{tag_name} ne 'li'),                    li => ($token->{tag_name} ne 'li'),
7529                      option => 1,
7530                      optgroup => 1,
7531                    p => 1,                    p => 1,
7532                      rt => 1,
7533                      rp => 1,
7534                   }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {                   }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7535              !!!cp ('t409');              !!!cp ('t409');
7536              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6570  sub _tree_construction_main ($) { Line 7541  sub _tree_construction_main ($) {
7541                    ne $token->{tag_name}) {                    ne $token->{tag_name}) {
7542              !!!cp ('t412');              !!!cp ('t412');
7543              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
7544                              value => $self->{open_elements}->[-1]->[0]                              text => $self->{open_elements}->[-1]->[0]
7545                                  ->manakai_local_name,                                  ->manakai_local_name,
7546                              token => $token);                              token => $token);
7547            } else {            } else {
# Line 6607  sub _tree_construction_main ($) { Line 7578  sub _tree_construction_main ($) {
7578    
7579          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7580            !!!cp ('t421');            !!!cp ('t421');
7581            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7582                              text => $token->{tag_name}, token => $token);
7583              ## NOTE: Ignore the token.
7584          } else {          } else {
7585            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7586            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
# Line 6620  sub _tree_construction_main ($) { Line 7593  sub _tree_construction_main ($) {
7593                    ne $token->{tag_name}) {                    ne $token->{tag_name}) {
7594              !!!cp ('t417.1');              !!!cp ('t417.1');
7595              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
7596                              value => $self->{open_elements}->[-1]->[0]                              text => $self->{open_elements}->[-1]->[0]
7597                                  ->manakai_local_name,                                  ->manakai_local_name,
7598                              token => $token);                              token => $token);
7599            } else {            } else {
# Line 6652  sub _tree_construction_main ($) { Line 7625  sub _tree_construction_main ($) {
7625    
7626          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7627            !!!cp ('t425.1');            !!!cp ('t425.1');
7628            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7629                              text => $token->{tag_name}, token => $token);
7630              ## NOTE: Ignore the token.
7631          } else {          } else {
7632            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7633            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
# Line 6664  sub _tree_construction_main ($) { Line 7639  sub _tree_construction_main ($) {
7639            if ($self->{open_elements}->[-1]->[0]->manakai_local_name            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7640                    ne $token->{tag_name}) {                    ne $token->{tag_name}) {
7641              !!!cp ('t425');              !!!cp ('t425');
7642              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
7643                                text => $token->{tag_name}, token => $token);
7644            } else {            } else {
7645              !!!cp ('t426');              !!!cp ('t426');
7646            }            }
# Line 6695  sub _tree_construction_main ($) { Line 7671  sub _tree_construction_main ($) {
7671                    ne $token->{tag_name}) {                    ne $token->{tag_name}) {
7672              !!!cp ('t412.1');              !!!cp ('t412.1');
7673              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
7674                              value => $self->{open_elements}->[-1]->[0]                              text => $self->{open_elements}->[-1]->[0]
7675                                  ->manakai_local_name,                                  ->manakai_local_name,
7676                              token => $token);                              token => $token);
7677            } else {            } else {
# Line 6705  sub _tree_construction_main ($) { Line 7681  sub _tree_construction_main ($) {
7681            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
7682          } else {          } else {
7683            !!!cp ('t413.1');            !!!cp ('t413.1');
7684            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7685                              text => $token->{tag_name}, token => $token);
7686    
7687            !!!cp ('t415.1');            !!!cp ('t415.1');
7688            ## As if <p>, then reprocess the current token            ## As if <p>, then reprocess the current token
# Line 6720  sub _tree_construction_main ($) { Line 7697  sub _tree_construction_main ($) {
7697        } elsif ({        } elsif ({
7698                  a => 1,                  a => 1,
7699                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
7700                  nobr => 1, s => 1, small => 1, strile => 1,                  nobr => 1, s => 1, small => 1, strike => 1,
7701                  strong => 1, tt => 1, u => 1,                  strong => 1, tt => 1, u => 1,
7702                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7703          !!!cp ('t427');          !!!cp ('t427');
# Line 6728  sub _tree_construction_main ($) { Line 7705  sub _tree_construction_main ($) {
7705          next B;          next B;
7706        } elsif ($token->{tag_name} eq 'br') {        } elsif ($token->{tag_name} eq 'br') {
7707          !!!cp ('t428');          !!!cp ('t428');
7708          !!!parse-error (type => 'unmatched end tag:br', token => $token);          !!!parse-error (type => 'unmatched end tag',
7709                            text => 'br', token => $token);
7710    
7711          ## As if <br>          ## As if <br>
7712          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
# Line 6753  sub _tree_construction_main ($) { Line 7731  sub _tree_construction_main ($) {
7731                  noscript => 0, ## TODO: if scripting is enabled                  noscript => 0, ## TODO: if scripting is enabled
7732                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7733          !!!cp ('t429');          !!!cp ('t429');
7734          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);          !!!parse-error (type => 'unmatched end tag',
7735                            text => $token->{tag_name}, token => $token);
7736          ## Ignore the token          ## Ignore the token
7737          !!!next-token;          !!!next-token;
7738          next B;          next B;
# Line 6772  sub _tree_construction_main ($) { Line 7751  sub _tree_construction_main ($) {
7751              ## generate implied end tags              ## generate implied end tags
7752              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7753                !!!cp ('t430');                !!!cp ('t430');
7754                ## ISSUE: Can this case be reached?                ## NOTE: |<ruby><rt></ruby>|.
7755                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7756                  ## which seems wrong.
7757                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
7758                  $node_i++;
7759              }              }
7760                    
7761              ## Step 2              ## Step 2
# Line 6782  sub _tree_construction_main ($) { Line 7764  sub _tree_construction_main ($) {
7764                !!!cp ('t431');                !!!cp ('t431');
7765                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
7766                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
7767                                value => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
7768                                    ->manakai_local_name,                                    ->manakai_local_name,
7769                                token => $token);                                token => $token);
7770              } else {              } else {
# Line 6790  sub _tree_construction_main ($) { Line 7772  sub _tree_construction_main ($) {
7772              }              }
7773                            
7774              ## Step 3              ## Step 3
7775              splice @{$self->{open_elements}}, $node_i;              splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7776    
7777              !!!next-token;              !!!next-token;
7778              last S2;              last S2;
# Line 6801  sub _tree_construction_main ($) { Line 7783  sub _tree_construction_main ($) {
7783                  ($node->[1] & SPECIAL_EL or                  ($node->[1] & SPECIAL_EL or
7784                   $node->[1] & SCOPING_EL)) {                   $node->[1] & SCOPING_EL)) {
7785                !!!cp ('t433');                !!!cp ('t433');
7786                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
7787                                  text => $token->{tag_name}, token => $token);
7788                ## Ignore the token                ## Ignore the token
7789                !!!next-token;                !!!next-token;
7790                last S2;                last S2;
             }  
7791    
7792                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7793                  ## 9.27, "a" is a child of <dd> (conforming).  In
7794                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7795                  ## "a" is a child of both <body> and <dd>.
7796                }
7797                
7798              !!!cp ('t434');              !!!cp ('t434');
7799            }            }
7800                        
# Line 6847  sub _tree_construction_main ($) { Line 7835  sub _tree_construction_main ($) {
7835    ## TODO: script stuffs    ## TODO: script stuffs
7836  } # _tree_construct_main  } # _tree_construct_main
7837    
7838  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
7839    my $class = shift;    my $class = shift;
7840    my $node = shift;    my $node = shift;
7841    my $s = \$_[0];    #my $s = \$_[0];
7842    my $onerror = $_[1];    my $onerror = $_[1];
7843      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
7844    
7845    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
7846    
# Line 6870  sub set_inner_html ($$$) { Line 7859  sub set_inner_html ($$$) {
7859      }      }
7860    
7861      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
7862      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
7863    } elsif ($nt == 1) {    } elsif ($nt == 1) {
7864      ## TODO: If non-html element      ## TODO: If non-html element
7865    
7866      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
7867    
7868    ## TODO: Support for $get_wrapper
7869    
7870      ## Step 1 # MUST      ## Step 1 # MUST
7871      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
7872      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 6887  sub set_inner_html ($$$) { Line 7878  sub set_inner_html ($$$) {
7878      my $i = 0;      my $i = 0;
7879      $p->{line_prev} = $p->{line} = 1;      $p->{line_prev} = $p->{line} = 1;
7880      $p->{column_prev} = $p->{column} = 0;      $p->{column_prev} = $p->{column} = 0;
7881      $p->{set_next_char} = sub {      require Whatpm::Charset::DecodeHandle;
7882        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
7883        $input = $get_wrapper->($input);
7884        $p->{set_nc} = sub {
7885        my $self = shift;        my $self = shift;
7886    
7887        pop @{$self->{prev_char}};        my $char = '';
7888        unshift @{$self->{prev_char}}, $self->{next_char};        if (defined $self->{next_nc}) {
7889            $char = $self->{next_nc};
7890        $self->{next_char} = -1 and return if $i >= length $$s;          delete $self->{next_nc};
7891        $self->{next_char} = ord substr $$s, $i++, 1;          $self->{nc} = ord $char;
7892          } else {
7893            $self->{char_buffer} = '';
7894            $self->{char_buffer_pos} = 0;
7895            
7896            my $count = $input->manakai_read_until
7897                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
7898                 $self->{char_buffer_pos});
7899            if ($count) {
7900              $self->{line_prev} = $self->{line};
7901              $self->{column_prev} = $self->{column};
7902              $self->{column}++;
7903              $self->{nc}
7904                  = ord substr ($self->{char_buffer},
7905                                $self->{char_buffer_pos}++, 1);
7906              return;
7907            }
7908            
7909            if ($input->read ($char, 1)) {
7910              $self->{nc} = ord $char;
7911            } else {
7912              $self->{nc} = -1;
7913              return;
7914            }
7915          }
7916    
7917        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
7918        $p->{column}++;        $p->{column}++;
7919    
7920        if ($self->{next_char} == 0x000A) { # LF        if ($self->{nc} == 0x000A) { # LF
7921          $p->{line}++;          $p->{line}++;
7922          $p->{column} = 0;          $p->{column} = 0;
7923          !!!cp ('i1');          !!!cp ('i1');
7924        } elsif ($self->{next_char} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
7925          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
7926          $self->{next_char} = 0x000A; # LF # MUST          my $next = '';
7927            if ($input->read ($next, 1) and $next ne "\x0A") {
7928              $self->{next_nc} = $next;
7929            }
7930            $self->{nc} = 0x000A; # LF # MUST
7931          $p->{line}++;          $p->{line}++;
7932          $p->{column} = 0;          $p->{column} = 0;
7933          !!!cp ('i2');          !!!cp ('i2');
7934        } 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  
7935          !!!cp ('i4');          !!!cp ('i4');
7936          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
7937          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
7938        }        }
7939      };      };
7940      $p->{prev_char} = [-1, -1, -1];  
7941      $p->{next_char} = -1;      $p->{read_until} = sub {
7942              #my ($scalar, $specials_range, $offset) = @_;
7943          return 0 if defined $p->{next_nc};
7944    
7945          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
7946          my $offset = $_[2] || 0;
7947          
7948          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
7949            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
7950            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
7951              substr ($_[0], $offset)
7952                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
7953              my $count = $+[0] - $-[0];
7954              if ($count) {
7955                $p->{column} += $count;
7956                $p->{char_buffer_pos} += $count;
7957                $p->{line_prev} = $p->{line};
7958                $p->{column_prev} = $p->{column} - 1;
7959                $p->{nc} = -1;
7960              }
7961              return $count;
7962            } else {
7963              return 0;
7964            }
7965          } else {
7966            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
7967            if ($count) {
7968              $p->{column} += $count;
7969              $p->{column_prev} += $count;
7970              $p->{nc} = -1;
7971            }
7972            return $count;
7973          }
7974        }; # $p->{read_until}
7975    
7976      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
7977        my (%opt) = @_;        my (%opt) = @_;
7978        my $line = $opt{line};        my $line = $opt{line};
# Line 6935  sub set_inner_html ($$$) { Line 7987  sub set_inner_html ($$$) {
7987        $ponerror->(line => $p->{line}, column => $p->{column}, @_);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
7988      };      };
7989            
7990        my $char_onerror = sub {
7991          my (undef, $type, %opt) = @_;
7992          $ponerror->(layer => 'encode',
7993                      line => $p->{line}, column => $p->{column} + 1,
7994                      %opt, type => $type);
7995        }; # $char_onerror
7996        $input->onerror ($char_onerror);
7997    
7998      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
7999      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
8000    

Legend:
Removed from v.1.127  
changed lines
  Added in v.1.194

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24