/[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.139 by wakaba, Sat May 24 04:26:27 2008 UTC revision 1.191 by wakaba, Mon Sep 22 06:04:29 2008 UTC
# Line 3  use strict; Line 3  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4  use Error qw(:try);  use Error qw(:try);
5    
6    ## NOTE: This module don't check all HTML5 parse errors; character
7    ## encoding related parse errors are expected to be handled by relevant
8    ## modules.
9    ## Parse errors for control characters that are not allowed in HTML5
10    ## documents, for surrogate code points, and for noncharacter code
11    ## points, as well as U+FFFD substitions for characters whose code points
12    ## is higher than U+10FFFF may be detected by combining the parser with
13    ## the checker implemented by Whatpm::Charset::UnicodeChecker (for its
14    ## usage example, see |t/HTML-tree.t| in the Whatpm package or the
15    ## WebHACC::Language::HTML module in the WebHACC package).
16    
17  ## ISSUE:  ## ISSUE:
18  ## var doc = implementation.createDocument (null, null, null);  ## var doc = implementation.createDocument (null, null, null);
19  ## doc.write ('');  ## doc.write ('');
20  ## alert (doc.compatMode);  ## alert (doc.compatMode);
21    
 ## TODO: 1252 parse error (revision 1264)  
 ## TODO: 8859-11 = 874 (revision 1271)  
   
22  require IO::Handle;  require IO::Handle;
23    
24  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
# Line 48  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 55  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 END_TAG_OPTIONAL_EL
70    ## is used in "generate implied end tags" implementation (search for the
71    ## function mae).
72  sub END_TAG_OPTIONAL_EL () {  sub END_TAG_OPTIONAL_EL () {
73    DD_EL |    DD_EL |
74    DT_EL |    DT_EL |
75    LI_EL |    LI_EL |
76    P_EL    P_EL |
77      RUBY_COMPONENT_EL
78  }  }
79    
80    ## NOTE: Used in </body> and EOF algorithms.
81  sub ALL_END_TAG_OPTIONAL_EL () {  sub ALL_END_TAG_OPTIONAL_EL () {
82    END_TAG_OPTIONAL_EL |    DD_EL |
83      DT_EL |
84      LI_EL |
85      P_EL |
86    
87    BODY_EL |    BODY_EL |
88    HTML_EL |    HTML_EL |
89    TABLE_CELL_EL |    TABLE_CELL_EL |
# Line 99  sub SPECIAL_EL () { Line 119  sub SPECIAL_EL () {
119    ADDRESS_EL |    ADDRESS_EL |
120    BODY_EL |    BODY_EL |
121    DIV_EL |    DIV_EL |
122    END_TAG_OPTIONAL_EL |  
123      DD_EL |
124      DT_EL |
125      LI_EL |
126      P_EL |
127    
128    FORM_EL |    FORM_EL |
129    FRAMESET_EL |    FRAMESET_EL |
130    HEADING_EL |    HEADING_EL |
# Line 173  my $el_category = { Line 198  my $el_category = {
198    param => MISC_SPECIAL_EL,    param => MISC_SPECIAL_EL,
199    plaintext => MISC_SPECIAL_EL,    plaintext => MISC_SPECIAL_EL,
200    pre => MISC_SPECIAL_EL,    pre => MISC_SPECIAL_EL,
201      rp => RUBY_COMPONENT_EL,
202      rt => RUBY_COMPONENT_EL,
203      ruby => RUBY_EL,
204    s => FORMATTING_EL,    s => FORMATTING_EL,
205    script => MISC_SPECIAL_EL,    script => MISC_SPECIAL_EL,
206    select => SELECT_EL,    select => SELECT_EL,
# Line 214  my $el_category_f = { Line 242  my $el_category_f = {
242  };  };
243    
244  my $svg_attr_name = {  my $svg_attr_name = {
245      attributename => 'attributeName',
246    attributetype => 'attributeType',    attributetype => 'attributeType',
247    basefrequency => 'baseFrequency',    basefrequency => 'baseFrequency',
248    baseprofile => 'baseProfile',    baseprofile => 'baseProfile',
# Line 224  my $svg_attr_name = { Line 253  my $svg_attr_name = {
253    diffuseconstant => 'diffuseConstant',    diffuseconstant => 'diffuseConstant',
254    edgemode => 'edgeMode',    edgemode => 'edgeMode',
255    externalresourcesrequired => 'externalResourcesRequired',    externalresourcesrequired => 'externalResourcesRequired',
   fecolormatrix => 'feColorMatrix',  
   fecomposite => 'feComposite',  
   fegaussianblur => 'feGaussianBlur',  
   femorphology => 'feMorphology',  
   fetile => 'feTile',  
256    filterres => 'filterRes',    filterres => 'filterRes',
257    filterunits => 'filterUnits',    filterunits => 'filterUnits',
258    glyphref => 'glyphRef',    glyphref => 'glyphRef',
# Line 262  my $svg_attr_name = { Line 286  my $svg_attr_name = {
286    repeatcount => 'repeatCount',    repeatcount => 'repeatCount',
287    repeatdur => 'repeatDur',    repeatdur => 'repeatDur',
288    requiredextensions => 'requiredExtensions',    requiredextensions => 'requiredExtensions',
289      requiredfeatures => 'requiredFeatures',
290    specularconstant => 'specularConstant',    specularconstant => 'specularConstant',
291    specularexponent => 'specularExponent',    specularexponent => 'specularExponent',
292    spreadmethod => 'spreadMethod',    spreadmethod => 'spreadMethod',
# Line 298  my $foreign_attr_xname = { Line 323  my $foreign_attr_xname = {
323    
324  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
325    
326  my $c1_entity_char = {  my $charref_map = {
327      0x0D => 0x000A,
328    0x80 => 0x20AC,    0x80 => 0x20AC,
329    0x81 => 0xFFFD,    0x81 => 0xFFFD,
330    0x82 => 0x201A,    0x82 => 0x201A,
# Line 331  my $c1_entity_char = { Line 357  my $c1_entity_char = {
357    0x9D => 0xFFFD,    0x9D => 0xFFFD,
358    0x9E => 0x017E,    0x9E => 0x017E,
359    0x9F => 0x0178,    0x9F => 0x0178,
360  }; # $c1_entity_char  }; # $charref_map
361    $charref_map->{$_} = 0xFFFD
362        for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
363            0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
364            0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
365            0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
366            0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
367            0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
368            0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
369    
370  sub parse_byte_string ($$$$;$) {  sub parse_byte_string ($$$$;$) {
371    my $self = shift;    my $self = shift;
# Line 340  sub parse_byte_string ($$$$;$) { Line 374  sub parse_byte_string ($$$$;$) {
374    return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);    return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
375  } # parse_byte_string  } # parse_byte_string
376    
377  sub parse_byte_stream ($$$$;$) {  sub parse_byte_stream ($$$$;$$) {
378      # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
379    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
380    my $charset_name = shift;    my $charset_name = shift;
381    my $byte_stream = $_[0];    my $byte_stream = $_[0];
# Line 351  sub parse_byte_stream ($$$$;$) { Line 386  sub parse_byte_stream ($$$$;$) {
386    };    };
387    $self->{parse_error} = $onerror; # updated later by parse_char_string    $self->{parse_error} = $onerror; # updated later by parse_char_string
388    
389      my $get_wrapper = $_[3] || sub ($) {
390        return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
391      };
392    
393    ## HTML5 encoding sniffing algorithm    ## HTML5 encoding sniffing algorithm
394    require Message::Charset::Info;    require Message::Charset::Info;
395    my $charset;    my $charset;
# Line 358  sub parse_byte_stream ($$$$;$) { Line 397  sub parse_byte_stream ($$$$;$) {
397    my ($char_stream, $e_status);    my ($char_stream, $e_status);
398    
399    SNIFFING: {    SNIFFING: {
400        ## NOTE: By setting |allow_fallback| option true when the
401        ## |get_decode_handle| method is invoked, we ignore what the HTML5
402        ## spec requires, i.e. unsupported encoding should be ignored.
403          ## TODO: We should not do this unless the parser is invoked
404          ## in the conformance checking mode, in which this behavior
405          ## would be useful.
406    
407      ## Step 1      ## Step 1
408      if (defined $charset_name) {      if (defined $charset_name) {
409        $charset = Message::Charset::Info->get_by_iana_name ($charset_name);        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
410              ## TODO: Is this ok?  Transfer protocol's parameter should be
411              ## interpreted in its semantics?
412    
       ## ISSUE: Unsupported encoding is not ignored according to the spec.  
413        ($char_stream, $e_status) = $charset->get_decode_handle        ($char_stream, $e_status) = $charset->get_decode_handle
414            ($byte_stream, allow_error_reporting => 1,            ($byte_stream, allow_error_reporting => 1,
415             allow_fallback => 1);             allow_fallback => 1);
# Line 371  sub parse_byte_stream ($$$$;$) { Line 417  sub parse_byte_stream ($$$$;$) {
417          $self->{confident} = 1;          $self->{confident} = 1;
418          last SNIFFING;          last SNIFFING;
419        } else {        } else {
420          ## TODO: unsupported error          !!!parse-error (type => 'charset:not supported',
421                            layer => 'encode',
422                            line => 1, column => 1,
423                            value => $charset_name,
424                            level => $self->{level}->{uncertain});
425        }        }
426      }      }
427    
# Line 385  sub parse_byte_stream ($$$$;$) { Line 435  sub parse_byte_stream ($$$$;$) {
435    
436      ## Step 3      ## Step 3
437      if ($byte_buffer =~ /^\xFE\xFF/) {      if ($byte_buffer =~ /^\xFE\xFF/) {
438        $charset = Message::Charset::Info->get_by_iana_name ('utf-16be');        $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
439        ($char_stream, $e_status) = $charset->get_decode_handle        ($char_stream, $e_status) = $charset->get_decode_handle
440            ($byte_stream, allow_error_reporting => 1,            ($byte_stream, allow_error_reporting => 1,
441             allow_fallback => 1, byte_buffer => \$byte_buffer);             allow_fallback => 1, byte_buffer => \$byte_buffer);
442        $self->{confident} = 1;        $self->{confident} = 1;
443        last SNIFFING;        last SNIFFING;
444      } elsif ($byte_buffer =~ /^\xFF\xFE/) {      } elsif ($byte_buffer =~ /^\xFF\xFE/) {
445        $charset = Message::Charset::Info->get_by_iana_name ('utf-16le');        $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
446        ($char_stream, $e_status) = $charset->get_decode_handle        ($char_stream, $e_status) = $charset->get_decode_handle
447            ($byte_stream, allow_error_reporting => 1,            ($byte_stream, allow_error_reporting => 1,
448             allow_fallback => 1, byte_buffer => \$byte_buffer);             allow_fallback => 1, byte_buffer => \$byte_buffer);
449        $self->{confident} = 1;        $self->{confident} = 1;
450        last SNIFFING;        last SNIFFING;
451      } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {      } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
452        $charset = Message::Charset::Info->get_by_iana_name ('utf-8');        $charset = Message::Charset::Info->get_by_html_name ('utf-8');
453        ($char_stream, $e_status) = $charset->get_decode_handle        ($char_stream, $e_status) = $charset->get_decode_handle
454            ($byte_stream, allow_error_reporting => 1,            ($byte_stream, allow_error_reporting => 1,
455             allow_fallback => 1, byte_buffer => \$byte_buffer);             allow_fallback => 1, byte_buffer => \$byte_buffer);
# Line 418  sub parse_byte_stream ($$$$;$) { Line 468  sub parse_byte_stream ($$$$;$) {
468      $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string      $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
469          ($byte_buffer);          ($byte_buffer);
470      if (defined $charset_name) {      if (defined $charset_name) {
471        $charset = Message::Charset::Info->get_by_iana_name ($charset_name);        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
472    
473        ## ISSUE: Unsupported encoding is not ignored according to the spec.        ## ISSUE: Unsupported encoding is not ignored according to the spec.
474        require Whatpm::Charset::DecodeHandle;        require Whatpm::Charset::DecodeHandle;
# Line 429  sub parse_byte_stream ($$$$;$) { Line 479  sub parse_byte_stream ($$$$;$) {
479             allow_fallback => 1, byte_buffer => \$byte_buffer);             allow_fallback => 1, byte_buffer => \$byte_buffer);
480        if ($char_stream) {        if ($char_stream) {
481          $buffer->{buffer} = $byte_buffer;          $buffer->{buffer} = $byte_buffer;
482          !!!parse-error (type => 'sniffing:chardet', ## TODO: type name          !!!parse-error (type => 'sniffing:chardet',
483                          value => $charset_name,                          text => $charset_name,
484                          level => $self->{info_level},                          level => $self->{level}->{info},
485                            layer => 'encode',
486                          line => 1, column => 1);                          line => 1, column => 1);
487          $self->{confident} = 0;          $self->{confident} = 0;
488          last SNIFFING;          last SNIFFING;
# Line 440  sub parse_byte_stream ($$$$;$) { Line 491  sub parse_byte_stream ($$$$;$) {
491    
492      ## Step 7: default      ## Step 7: default
493      ## TODO: Make this configurable.      ## TODO: Make this configurable.
494      $charset = Message::Charset::Info->get_by_iana_name ('windows-1252');      $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
495          ## NOTE: We choose |windows-1252| here, since |utf-8| should be          ## NOTE: We choose |windows-1252| here, since |utf-8| should be
496          ## detectable in the step 6.          ## detectable in the step 6.
497      require Whatpm::Charset::DecodeHandle;      require Whatpm::Charset::DecodeHandle;
# Line 452  sub parse_byte_stream ($$$$;$) { Line 503  sub parse_byte_stream ($$$$;$) {
503                                         allow_fallback => 1,                                         allow_fallback => 1,
504                                         byte_buffer => \$byte_buffer);                                         byte_buffer => \$byte_buffer);
505      $buffer->{buffer} = $byte_buffer;      $buffer->{buffer} = $byte_buffer;
506      !!!parse-error (type => 'sniffing:default', ## TODO: type name      !!!parse-error (type => 'sniffing:default',
507                      value => 'windows-1252',                      text => 'windows-1252',
508                      level => $self->{info_level},                      level => $self->{level}->{info},
509                      line => 1, column => 1);                      line => 1, column => 1,
510                        layer => 'encode');
511      $self->{confident} = 0;      $self->{confident} = 0;
512    } # SNIFFING    } # SNIFFING
513    
   $self->{input_encoding} = $charset->get_iana_name;  
514    if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {    if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
515      !!!parse-error (type => 'chardecode:fallback', ## TODO: type name      $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
516                      value => $self->{input_encoding},      !!!parse-error (type => 'chardecode:fallback',
517                      level => $self->{unsupported_level},                      #text => $self->{input_encoding},
518                      line => 1, column => 1);                      level => $self->{level}->{uncertain},
519                        line => 1, column => 1,
520                        layer => 'encode');
521    } elsif (not ($e_status &    } elsif (not ($e_status &
522                  Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL())) {                  Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
523      !!!parse-error (type => 'chardecode:no error', ## TODO: type name      $self->{input_encoding} = $charset->get_iana_name;
524                      value => $self->{input_encoding},      !!!parse-error (type => 'chardecode:no error',
525                      level => $self->{unsupported_level},                      text => $self->{input_encoding},
526                      line => 1, column => 1);                      level => $self->{level}->{uncertain},
527                        line => 1, column => 1,
528                        layer => 'encode');
529      } else {
530        $self->{input_encoding} = $charset->get_iana_name;
531    }    }
532    
533    $self->{change_encoding} = sub {    $self->{change_encoding} = sub {
# Line 478  sub parse_byte_stream ($$$$;$) { Line 535  sub parse_byte_stream ($$$$;$) {
535      $charset_name = shift;      $charset_name = shift;
536      my $token = shift;      my $token = shift;
537    
538      $charset = Message::Charset::Info->get_by_iana_name ($charset_name);      $charset = Message::Charset::Info->get_by_html_name ($charset_name);
539      ($char_stream, $e_status) = $charset->get_decode_handle      ($char_stream, $e_status) = $charset->get_decode_handle
540          ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,          ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
541           byte_buffer => \ $buffer->{buffer});           byte_buffer => \ $buffer->{buffer});
# Line 487  sub parse_byte_stream ($$$$;$) { Line 544  sub parse_byte_stream ($$$$;$) {
544        ## "Change the encoding" algorithm:        ## "Change the encoding" algorithm:
545    
546        ## Step 1            ## Step 1    
547        if ($charset->{iana_names}->{'utf-16'}) { ## ISSUE: UTF-16BE -> UTF-8? UTF-16LE -> UTF-8?        if ($charset->{category} &
548          $charset = Message::Charset::Info->get_by_iana_name ('utf-8');            Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
549            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
550          ($char_stream, $e_status) = $charset->get_decode_handle          ($char_stream, $e_status) = $charset->get_decode_handle
551              ($byte_stream,              ($byte_stream,
552               byte_buffer => \ $buffer->{buffer});               byte_buffer => \ $buffer->{buffer});
# Line 498  sub parse_byte_stream ($$$$;$) { Line 556  sub parse_byte_stream ($$$$;$) {
556        ## Step 2        ## Step 2
557        if (defined $self->{input_encoding} and        if (defined $self->{input_encoding} and
558            $self->{input_encoding} eq $charset_name) {            $self->{input_encoding} eq $charset_name) {
559          !!!parse-error (type => 'charset label:matching', ## TODO: type          !!!parse-error (type => 'charset label:matching',
560                          value => $charset_name,                          text => $charset_name,
561                          level => $self->{info_level});                          level => $self->{level}->{info});
562          $self->{confident} = 1;          $self->{confident} = 1;
563          return;          return;
564        }        }
565    
566        !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}.        !!!parse-error (type => 'charset label detected',
567            ':'.$charset_name, level => 'w', token => $token);                        text => $self->{input_encoding},
568                          value => $charset_name,
569                          level => $self->{level}->{warn},
570                          token => $token);
571                
572        ## Step 3        ## Step 3
573        # if (can) {        # if (can) {
# Line 522  sub parse_byte_stream ($$$$;$) { Line 583  sub parse_byte_stream ($$$$;$) {
583    
584    my $char_onerror = sub {    my $char_onerror = sub {
585      my (undef, $type, %opt) = @_;      my (undef, $type, %opt) = @_;
586      !!!parse-error (%opt, type => $type,      !!!parse-error (layer => 'encode',
587                      line => $self->{line}, column => $self->{column} + 1);                      line => $self->{line}, column => $self->{column} + 1,
588                        %opt, type => $type);
589      if ($opt{octets}) {      if ($opt{octets}) {
590        ${$opt{octets}} = "\x{FFFD}"; # relacement character        ${$opt{octets}} = "\x{FFFD}"; # relacement character
591      }      }
592    };    };
   $char_stream->onerror ($char_onerror);  
593    
594    my @args = @_; shift @args; # $s    my $wrapped_char_stream = $get_wrapper->($char_stream);
595      $wrapped_char_stream->onerror ($char_onerror);
596    
597      my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
598    my $return;    my $return;
599    try {    try {
600      $return = $self->parse_char_stream ($char_stream, @args);        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
601    } catch Whatpm::HTML::RestartParser with {    } catch Whatpm::HTML::RestartParser with {
602      ## NOTE: Invoked after {change_encoding}.      ## NOTE: Invoked after {change_encoding}.
603    
     $self->{input_encoding} = $charset->get_iana_name;  
604      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
605        !!!parse-error (type => 'chardecode:fallback', ## TODO: type name        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
606                        value => $self->{input_encoding},        !!!parse-error (type => 'chardecode:fallback',
607                        level => $self->{unsupported_level},                        level => $self->{level}->{uncertain},
608                        line => 1, column => 1);                        #text => $self->{input_encoding},
609                          line => 1, column => 1,
610                          layer => 'encode');
611      } elsif (not ($e_status &      } elsif (not ($e_status &
612                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL())) {                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
613        !!!parse-error (type => 'chardecode:no error', ## TODO: type name        $self->{input_encoding} = $charset->get_iana_name;
614                        value => $self->{input_encoding},        !!!parse-error (type => 'chardecode:no error',
615                        level => $self->{unsupported_level},                        text => $self->{input_encoding},
616                        line => 1, column => 1);                        level => $self->{level}->{uncertain},
617                          line => 1, column => 1,
618                          layer => 'encode');
619        } else {
620          $self->{input_encoding} = $charset->get_iana_name;
621      }      }
622      $self->{confident} = 1;      $self->{confident} = 1;
623      $char_stream->onerror ($char_onerror);  
624      $return = $self->parse_char_stream ($char_stream, @args);      $wrapped_char_stream = $get_wrapper->($char_stream);
625        $wrapped_char_stream->onerror ($char_onerror);
626    
627        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
628    };    };
629    return $return;    return $return;
630  } # parse_byte_stream  } # parse_byte_stream
# Line 566  sub parse_byte_stream ($$$$;$) { Line 638  sub parse_byte_stream ($$$$;$) {
638  ## such as |parse_byte_string| in this module, must ensure that it does  ## such as |parse_byte_string| in this module, must ensure that it does
639  ## strip the BOM and never strip any ZWNBSP.  ## strip the BOM and never strip any ZWNBSP.
640    
641  sub parse_char_string ($$$;$) {  sub parse_char_string ($$$;$$) {
642      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
643    my $self = shift;    my $self = shift;
   require utf8;  
644    my $s = ref $_[0] ? $_[0] : \($_[0]);    my $s = ref $_[0] ? $_[0] : \($_[0]);
645    open my $input, '<' . (utf8::is_utf8 ($$s) ? ':utf8' : ''), $s;    require Whatpm::Charset::DecodeHandle;
646      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
647    return $self->parse_char_stream ($input, @_[1..$#_]);    return $self->parse_char_stream ($input, @_[1..$#_]);
648  } # parse_char_string  } # parse_char_string
649  *parse_string = \&parse_char_string;  *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
650    
651  sub parse_char_stream ($$$;$) {  sub parse_char_stream ($$$;$$) {
652    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
653    my $input = $_[0];    my $input = $_[0];
654    $self->{document} = $_[1];    $self->{document} = $_[1];
# Line 586  sub parse_char_stream ($$$;$) { Line 659  sub parse_char_stream ($$$;$) {
659    $self->{confident} = 1 unless exists $self->{confident};    $self->{confident} = 1 unless exists $self->{confident};
660    $self->{document}->input_encoding ($self->{input_encoding})    $self->{document}->input_encoding ($self->{input_encoding})
661        if defined $self->{input_encoding};        if defined $self->{input_encoding};
662    ## TODO: |{input_encoding}| is needless?
663    
   my $i = 0;  
664    $self->{line_prev} = $self->{line} = 1;    $self->{line_prev} = $self->{line} = 1;
665    $self->{column_prev} = $self->{column} = 0;    $self->{column_prev} = -1;
666    $self->{set_next_char} = sub {    $self->{column} = 0;
667      $self->{set_nc} = sub {
668      my $self = shift;      my $self = shift;
669    
670      pop @{$self->{prev_char}};      my $char = '';
671      unshift @{$self->{prev_char}}, $self->{next_char};      if (defined $self->{next_nc}) {
672          $char = $self->{next_nc};
673      my $char;        delete $self->{next_nc};
674      if (defined $self->{next_next_char}) {        $self->{nc} = ord $char;
       $char = $self->{next_next_char};  
       delete $self->{next_next_char};  
675      } else {      } else {
676        $char = $input->getc;        $self->{char_buffer} = '';
677          $self->{char_buffer_pos} = 0;
678    
679          my $count = $input->manakai_read_until
680             ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
681          if ($count) {
682            $self->{line_prev} = $self->{line};
683            $self->{column_prev} = $self->{column};
684            $self->{column}++;
685            $self->{nc}
686                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
687            return;
688          }
689    
690          if ($input->read ($char, 1)) {
691            $self->{nc} = ord $char;
692          } else {
693            $self->{nc} = -1;
694            return;
695          }
696      }      }
     $self->{next_char} = -1 and return unless defined $char;  
     $self->{next_char} = ord $char;  
697    
698      ($self->{line_prev}, $self->{column_prev})      ($self->{line_prev}, $self->{column_prev})
699          = ($self->{line}, $self->{column});          = ($self->{line}, $self->{column});
700      $self->{column}++;      $self->{column}++;
701            
702      if ($self->{next_char} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
703        !!!cp ('j1');        !!!cp ('j1');
704        $self->{line}++;        $self->{line}++;
705        $self->{column} = 0;        $self->{column} = 0;
706      } elsif ($self->{next_char} == 0x000D) { # CR      } elsif ($self->{nc} == 0x000D) { # CR
707        !!!cp ('j2');        !!!cp ('j2');
708        my $next = $input->getc;  ## TODO: support for abort/streaming
709        if (defined $next and $next ne "\x0A") {        my $next = '';
710          $self->{next_next_char} = $next;        if ($input->read ($next, 1) and $next ne "\x0A") {
711            $self->{next_nc} = $next;
712        }        }
713        $self->{next_char} = 0x000A; # LF # MUST        $self->{nc} = 0x000A; # LF # MUST
714        $self->{line}++;        $self->{line}++;
715        $self->{column} = 0;        $self->{column} = 0;
716      } elsif ($self->{next_char} > 0x10FFFF) {      } elsif ($self->{nc} == 0x0000) { # NULL
       !!!cp ('j3');  
       $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST  
     } elsif ($self->{next_char} == 0x0000) { # NULL  
717        !!!cp ('j4');        !!!cp ('j4');
718        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
719        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
     } elsif ($self->{next_char} <= 0x0008 or  
              (0x000E <= $self->{next_char} and $self->{next_char} <= 0x001F) or  
              (0x007F <= $self->{next_char} and $self->{next_char} <= 0x009F) or  
              (0xD800 <= $self->{next_char} and $self->{next_char} <= 0xDFFF) or  
              (0xFDD0 <= $self->{next_char} and $self->{next_char} <= 0xFDDF) or  
              {  
               0xFFFE => 1, 0xFFFF => 1, 0x1FFFE => 1, 0x1FFFF => 1,  
               0x2FFFE => 1, 0x2FFFF => 1, 0x3FFFE => 1, 0x3FFFF => 1,  
               0x4FFFE => 1, 0x4FFFF => 1, 0x5FFFE => 1, 0x5FFFF => 1,  
               0x6FFFE => 1, 0x6FFFF => 1, 0x7FFFE => 1, 0x7FFFF => 1,  
               0x8FFFE => 1, 0x8FFFF => 1, 0x9FFFE => 1, 0x9FFFF => 1,  
               0xAFFFE => 1, 0xAFFFF => 1, 0xBFFFE => 1, 0xBFFFF => 1,  
               0xCFFFE => 1, 0xCFFFF => 1, 0xDFFFE => 1, 0xDFFFF => 1,  
               0xEFFFE => 1, 0xEFFFF => 1, 0xFFFFE => 1, 0xFFFFF => 1,  
               0x10FFFE => 1, 0x10FFFF => 1,  
              }->{$self->{next_char}}) {  
       !!!cp ('j5');  
       !!!parse-error (type => 'control char', level => $self->{must_level});  
 ## TODO: error type documentation  
720      }      }
721    };    };
722    $self->{prev_char} = [-1, -1, -1];  
723    $self->{next_char} = -1;    $self->{read_until} = sub {
724        #my ($scalar, $specials_range, $offset) = @_;
725        return 0 if defined $self->{next_nc};
726    
727        my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
728        my $offset = $_[2] || 0;
729    
730        if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
731          pos ($self->{char_buffer}) = $self->{char_buffer_pos};
732          if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
733            substr ($_[0], $offset)
734                = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
735            my $count = $+[0] - $-[0];
736            if ($count) {
737              $self->{column} += $count;
738              $self->{char_buffer_pos} += $count;
739              $self->{line_prev} = $self->{line};
740              $self->{column_prev} = $self->{column} - 1;
741              $self->{nc} = -1;
742            }
743            return $count;
744          } else {
745            return 0;
746          }
747        } else {
748          my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
749          if ($count) {
750            $self->{column} += $count;
751            $self->{line_prev} = $self->{line};
752            $self->{column_prev} = $self->{column} - 1;
753            $self->{nc} = -1;
754          }
755          return $count;
756        }
757      }; # $self->{read_until}
758    
759    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
760      my (%opt) = @_;      my (%opt) = @_;
# Line 664  sub parse_char_stream ($$$;$) { Line 766  sub parse_char_stream ($$$;$) {
766      $onerror->(line => $self->{line}, column => $self->{column}, @_);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
767    };    };
768    
769      my $char_onerror = sub {
770        my (undef, $type, %opt) = @_;
771        !!!parse-error (layer => 'encode',
772                        line => $self->{line}, column => $self->{column} + 1,
773                        %opt, type => $type);
774      }; # $char_onerror
775    
776      if ($_[3]) {
777        $input = $_[3]->($input);
778        $input->onerror ($char_onerror);
779      } else {
780        $input->onerror ($char_onerror) unless defined $input->onerror;
781      }
782    
783    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
784    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
785    $self->_construct_tree;    $self->_construct_tree;
# Line 677  sub parse_char_stream ($$$;$) { Line 793  sub parse_char_stream ($$$;$) {
793  sub new ($) {  sub new ($) {
794    my $class = shift;    my $class = shift;
795    my $self = bless {    my $self = bless {
796      must_level => 'm',      level => {must => 'm',
797      should_level => 's',                should => 's',
798      good_level => 'w',                warn => 'w',
799      warn_level => 'w',                info => 'i',
800      info_level => 'i',                uncertain => 'u'},
     unsupported_level => 'u',  
801    }, $class;    }, $class;
802    $self->{set_next_char} = sub {    $self->{set_nc} = sub {
803      $self->{next_char} = -1;      $self->{nc} = -1;
804    };    };
805    $self->{parse_error} = sub {    $self->{parse_error} = sub {
806      #      #
# Line 712  sub RCDATA_CONTENT_MODEL () { CM_ENTITY Line 827  sub RCDATA_CONTENT_MODEL () { CM_ENTITY
827  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
828    
829  sub DATA_STATE () { 0 }  sub DATA_STATE () { 0 }
830  sub ENTITY_DATA_STATE () { 1 }  #sub ENTITY_DATA_STATE () { 1 }
831  sub TAG_OPEN_STATE () { 2 }  sub TAG_OPEN_STATE () { 2 }
832  sub CLOSE_TAG_OPEN_STATE () { 3 }  sub CLOSE_TAG_OPEN_STATE () { 3 }
833  sub TAG_NAME_STATE () { 4 }  sub TAG_NAME_STATE () { 4 }
# Line 723  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 Line 838  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8
838  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
839  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
840  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
841  sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }  #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
842  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
843  sub COMMENT_START_STATE () { 14 }  sub COMMENT_START_STATE () { 14 }
844  sub COMMENT_START_DASH_STATE () { 15 }  sub COMMENT_START_DASH_STATE () { 15 }
# Line 746  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STAT Line 861  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STAT
861  sub BOGUS_DOCTYPE_STATE () { 32 }  sub BOGUS_DOCTYPE_STATE () { 32 }
862  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
863  sub SELF_CLOSING_START_TAG_STATE () { 34 }  sub SELF_CLOSING_START_TAG_STATE () { 34 }
864  sub CDATA_BLOCK_STATE () { 35 }  sub CDATA_SECTION_STATE () { 35 }
865    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
866    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
867    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
868    sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
869    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
870    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
871    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
872    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
873    ## NOTE: "Entity data state", "entity in attribute value state", and
874    ## "consume a character reference" algorithm are jointly implemented
875    ## using the following six states:
876    sub ENTITY_STATE () { 44 }
877    sub ENTITY_HASH_STATE () { 45 }
878    sub NCR_NUM_STATE () { 46 }
879    sub HEXREF_X_STATE () { 47 }
880    sub HEXREF_HEX_STATE () { 48 }
881    sub ENTITY_NAME_STATE () { 49 }
882    sub PCDATA_STATE () { 50 } # "data state" in the spec
883    
884  sub DOCTYPE_TOKEN () { 1 }  sub DOCTYPE_TOKEN () { 1 }
885  sub COMMENT_TOKEN () { 2 }  sub COMMENT_TOKEN () { 2 }
# Line 799  sub IN_COLUMN_GROUP_IM () { 0b10 } Line 932  sub IN_COLUMN_GROUP_IM () { 0b10 }
932  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
933    my $self = shift;    my $self = shift;
934    $self->{state} = DATA_STATE; # MUST    $self->{state} = DATA_STATE; # MUST
935      #$self->{s_kwd}; # state keyword - initialized when used
936      #$self->{entity__value}; # initialized when used
937      #$self->{entity__match}; # initialized when used
938    $self->{content_model} = PCDATA_CONTENT_MODEL; # be    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
939    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    undef $self->{ct}; # current token
940    undef $self->{current_attribute};    undef $self->{ca}; # current attribute
941    undef $self->{last_emitted_start_tag_name};    undef $self->{last_stag_name}; # last emitted start tag name
942    undef $self->{last_attribute_value_state};    #$self->{prev_state}; # initialized when used
943    delete $self->{self_closing};    delete $self->{self_closing};
944    $self->{char} = [];    $self->{char_buffer} = '';
945    # $self->{next_char}    $self->{char_buffer_pos} = 0;
946      $self->{nc} = -1; # next input character
947      #$self->{next_nc}
948    !!!next-input-character;    !!!next-input-character;
949    $self->{token} = [];    $self->{token} = [];
950    # $self->{escape}    # $self->{escape}
# Line 817  sub _initialize_tokenizer ($) { Line 955  sub _initialize_tokenizer ($) {
955  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
956  ##   ->{name} (DOCTYPE_TOKEN)  ##   ->{name} (DOCTYPE_TOKEN)
957  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
958  ##   ->{public_identifier} (DOCTYPE_TOKEN)  ##   ->{pubid} (DOCTYPE_TOKEN)
959  ##   ->{system_identifier} (DOCTYPE_TOKEN)  ##   ->{sysid} (DOCTYPE_TOKEN)
960  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
961  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
962  ##        ->{name}  ##        ->{name}
# Line 829  sub _initialize_tokenizer ($) { Line 967  sub _initialize_tokenizer ($) {
967  ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|  ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
968  ##     while the token is pushed back to the stack.  ##     while the token is pushed back to the stack.
969    
 ## 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?  
   
970  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
971    
972  ## 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 841  sub _initialize_tokenizer ($) { Line 975  sub _initialize_tokenizer ($) {
975  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
976  ## and removed from the list.  ## and removed from the list.
977    
978  ## NOTE: HTML5 "Writing HTML documents" section, applied to  ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
979  ## documents and not to user agents and conformance checkers,  ## (This requirement was dropped from HTML5 spec, unfortunately.)
980  ## contains some requirements that are not detected by the  
981  ## parsing algorithm:  my $is_space = {
982  ## - Some requirements on character encoding declarations. ## TODO    0x0009 => 1, # CHARACTER TABULATION (HT)
983  ## - "Elements MUST NOT contain content that their content model disallows."    0x000A => 1, # LINE FEED (LF)
984  ##   ... Some are parse error, some are not (will be reported by c.c.).    #0x000B => 0, # LINE TABULATION (VT)
985  ## - Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) ## TODO    0x000C => 1, # FORM FEED (FF)
986  ## - Text (in elements, attributes, and comments) SHOULD NOT contain    #0x000D => 1, # CARRIAGE RETURN (CR)
987  ##   control characters other than space characters. ## TODO: (what is control character? C0, C1 and DEL?  Unicode control character?)    0x0020 => 1, # SPACE (SP)
988    };
 ## TODO: HTML5 poses authors two SHOULD-level requirements that cannot  
 ## be detected by the HTML5 parsing algorithm:  
 ## - Text,  
989    
990  sub _get_next_token ($) {  sub _get_next_token ($) {
991    my $self = shift;    my $self = shift;
992    
993    if ($self->{self_closing}) {    if ($self->{self_closing}) {
994      !!!parse-error (type => 'nestc', token => $self->{current_token});      !!!parse-error (type => 'nestc', token => $self->{ct});
995      ## NOTE: The |self_closing| flag is only set by start tag token.      ## NOTE: The |self_closing| flag is only set by start tag token.
996      ## In addition, when a start tag token is emitted, it is always set to      ## In addition, when a start tag token is emitted, it is always set to
997      ## |current_token|.      ## |ct|.
998      delete $self->{self_closing};      delete $self->{self_closing};
999    }    }
1000    
# Line 873  sub _get_next_token ($) { Line 1004  sub _get_next_token ($) {
1004    }    }
1005    
1006    A: {    A: {
1007      if ($self->{state} == DATA_STATE) {      if ($self->{state} == PCDATA_STATE) {
1008        if ($self->{next_char} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1009    
1010          if ($self->{nc} == 0x0026) { # &
1011            !!!cp (0.1);
1012            ## NOTE: In the spec, the tokenizer is switched to the
1013            ## "entity data state".  In this implementation, the tokenizer
1014            ## is switched to the |ENTITY_STATE|, which is an implementation
1015            ## of the "consume a character reference" algorithm.
1016            $self->{entity_add} = -1;
1017            $self->{prev_state} = DATA_STATE;
1018            $self->{state} = ENTITY_STATE;
1019            !!!next-input-character;
1020            redo A;
1021          } elsif ($self->{nc} == 0x003C) { # <
1022            !!!cp (0.2);
1023            $self->{state} = TAG_OPEN_STATE;
1024            !!!next-input-character;
1025            redo A;
1026          } elsif ($self->{nc} == -1) {
1027            !!!cp (0.3);
1028            !!!emit ({type => END_OF_FILE_TOKEN,
1029                      line => $self->{line}, column => $self->{column}});
1030            last A; ## TODO: ok?
1031          } else {
1032            !!!cp (0.4);
1033            #
1034          }
1035    
1036          # Anything else
1037          my $token = {type => CHARACTER_TOKEN,
1038                       data => chr $self->{nc},
1039                       line => $self->{line}, column => $self->{column},
1040                      };
1041          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1042    
1043          ## Stay in the state.
1044          !!!next-input-character;
1045          !!!emit ($token);
1046          redo A;
1047        } elsif ($self->{state} == DATA_STATE) {
1048          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1049          if ($self->{nc} == 0x0026) { # &
1050            $self->{s_kwd} = '';
1051          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1052              not $self->{escape}) {              not $self->{escape}) {
1053            !!!cp (1);            !!!cp (1);
1054            $self->{state} = ENTITY_DATA_STATE;            ## NOTE: In the spec, the tokenizer is switched to the
1055              ## "entity data state".  In this implementation, the tokenizer
1056              ## is switched to the |ENTITY_STATE|, which is an implementation
1057              ## of the "consume a character reference" algorithm.
1058              $self->{entity_add} = -1;
1059              $self->{prev_state} = DATA_STATE;
1060              $self->{state} = ENTITY_STATE;
1061            !!!next-input-character;            !!!next-input-character;
1062            redo A;            redo A;
1063          } else {          } else {
1064            !!!cp (2);            !!!cp (2);
1065            #            #
1066          }          }
1067        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
1068          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1069            unless ($self->{escape}) {            $self->{s_kwd} .= '-';
1070              if ($self->{prev_char}->[0] == 0x002D and # -            
1071                  $self->{prev_char}->[1] == 0x0021 and # !            if ($self->{s_kwd} eq '<!--') {
1072                  $self->{prev_char}->[2] == 0x003C) { # <              !!!cp (3);
1073                !!!cp (3);              $self->{escape} = 1; # unless $self->{escape};
1074                $self->{escape} = 1;              $self->{s_kwd} = '--';
1075              } else {              #
1076                !!!cp (4);            } elsif ($self->{s_kwd} eq '---') {
1077              }              !!!cp (4);
1078                $self->{s_kwd} = '--';
1079                #
1080            } else {            } else {
1081              !!!cp (5);              !!!cp (5);
1082                #
1083            }            }
1084          }          }
1085                    
1086          #          #
1087        } elsif ($self->{next_char} == 0x003C) { # <        } elsif ($self->{nc} == 0x0021) { # !
1088            if (length $self->{s_kwd}) {
1089              !!!cp (5.1);
1090              $self->{s_kwd} .= '!';
1091              #
1092            } else {
1093              !!!cp (5.2);
1094              #$self->{s_kwd} = '';
1095              #
1096            }
1097            #
1098          } elsif ($self->{nc} == 0x003C) { # <
1099          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1100              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1101               not $self->{escape})) {               not $self->{escape})) {
# Line 912  sub _get_next_token ($) { Line 1105  sub _get_next_token ($) {
1105            redo A;            redo A;
1106          } else {          } else {
1107            !!!cp (7);            !!!cp (7);
1108              $self->{s_kwd} = '';
1109            #            #
1110          }          }
1111        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1112          if ($self->{escape} and          if ($self->{escape} and
1113              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1114            if ($self->{prev_char}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '--') {
               $self->{prev_char}->[1] == 0x002D) { # -  
1115              !!!cp (8);              !!!cp (8);
1116              delete $self->{escape};              delete $self->{escape};
1117            } else {            } else {
# Line 928  sub _get_next_token ($) { Line 1121  sub _get_next_token ($) {
1121            !!!cp (10);            !!!cp (10);
1122          }          }
1123                    
1124            $self->{s_kwd} = '';
1125          #          #
1126        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1127          !!!cp (11);          !!!cp (11);
1128            $self->{s_kwd} = '';
1129          !!!emit ({type => END_OF_FILE_TOKEN,          !!!emit ({type => END_OF_FILE_TOKEN,
1130                    line => $self->{line}, column => $self->{column}});                    line => $self->{line}, column => $self->{column}});
1131          last A; ## TODO: ok?          last A; ## TODO: ok?
1132        } else {        } else {
1133          !!!cp (12);          !!!cp (12);
1134            $self->{s_kwd} = '';
1135            #
1136        }        }
1137    
1138        # Anything else        # Anything else
1139        my $token = {type => CHARACTER_TOKEN,        my $token = {type => CHARACTER_TOKEN,
1140                     data => chr $self->{next_char},                     data => chr $self->{nc},
1141                     line => $self->{line}, column => $self->{column},                     line => $self->{line}, column => $self->{column},
1142                    };                    };
1143        ## Stay in the data state        if ($self->{read_until}->($token->{data}, q[-!<>&],
1144        !!!next-input-character;                                  length $token->{data})) {
1145            $self->{s_kwd} = '';
1146        !!!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  
1147    
1148        unless (defined $token) {        ## Stay in the data state.
1149          if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1150          !!!cp (13);          !!!cp (13);
1151          !!!emit ({type => CHARACTER_TOKEN, data => '&',          $self->{state} = PCDATA_STATE;
                   line => $l, column => $c,  
                  });  
1152        } else {        } else {
1153          !!!cp (14);          !!!cp (14);
1154          !!!emit ($token);          ## Stay in the state.
1155        }        }
1156          !!!next-input-character;
1157          !!!emit ($token);
1158        redo A;        redo A;
1159      } elsif ($self->{state} == TAG_OPEN_STATE) {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1160        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1161          if ($self->{next_char} == 0x002F) { # /          if ($self->{nc} == 0x002F) { # /
1162            !!!cp (15);            !!!cp (15);
1163            !!!next-input-character;            !!!next-input-character;
1164            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1165            redo A;            redo A;
1166            } elsif ($self->{nc} == 0x0021) { # !
1167              !!!cp (15.1);
1168              $self->{s_kwd} = '<' unless $self->{escape};
1169              #
1170          } else {          } else {
1171            !!!cp (16);            !!!cp (16);
1172            ## reconsume            #
           $self->{state} = DATA_STATE;  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev},  
                    });  
   
           redo A;  
1173          }          }
1174    
1175            ## reconsume
1176            $self->{state} = DATA_STATE;
1177            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1178                      line => $self->{line_prev},
1179                      column => $self->{column_prev},
1180                     });
1181            redo A;
1182        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1183          if ($self->{next_char} == 0x0021) { # !          if ($self->{nc} == 0x0021) { # !
1184            !!!cp (17);            !!!cp (17);
1185            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1186            !!!next-input-character;            !!!next-input-character;
1187            redo A;            redo A;
1188          } elsif ($self->{next_char} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1189            !!!cp (18);            !!!cp (18);
1190            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1191            !!!next-input-character;            !!!next-input-character;
1192            redo A;            redo A;
1193          } elsif (0x0041 <= $self->{next_char} and          } elsif (0x0041 <= $self->{nc} and
1194                   $self->{next_char} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1195            !!!cp (19);            !!!cp (19);
1196            $self->{current_token}            $self->{ct}
1197              = {type => START_TAG_TOKEN,              = {type => START_TAG_TOKEN,
1198                 tag_name => chr ($self->{next_char} + 0x0020),                 tag_name => chr ($self->{nc} + 0x0020),
1199                 line => $self->{line_prev},                 line => $self->{line_prev},
1200                 column => $self->{column_prev}};                 column => $self->{column_prev}};
1201            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1202            !!!next-input-character;            !!!next-input-character;
1203            redo A;            redo A;
1204          } elsif (0x0061 <= $self->{next_char} and          } elsif (0x0061 <= $self->{nc} and
1205                   $self->{next_char} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1206            !!!cp (20);            !!!cp (20);
1207            $self->{current_token} = {type => START_TAG_TOKEN,            $self->{ct} = {type => START_TAG_TOKEN,
1208                                      tag_name => chr ($self->{next_char}),                                      tag_name => chr ($self->{nc}),
1209                                      line => $self->{line_prev},                                      line => $self->{line_prev},
1210                                      column => $self->{column_prev}};                                      column => $self->{column_prev}};
1211            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1212            !!!next-input-character;            !!!next-input-character;
1213            redo A;            redo A;
1214          } elsif ($self->{next_char} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1215            !!!cp (21);            !!!cp (21);
1216            !!!parse-error (type => 'empty start tag',            !!!parse-error (type => 'empty start tag',
1217                            line => $self->{line_prev},                            line => $self->{line_prev},
# Line 1034  sub _get_next_token ($) { Line 1225  sub _get_next_token ($) {
1225                     });                     });
1226    
1227            redo A;            redo A;
1228          } elsif ($self->{next_char} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1229            !!!cp (22);            !!!cp (22);
1230            !!!parse-error (type => 'pio',            !!!parse-error (type => 'pio',
1231                            line => $self->{line_prev},                            line => $self->{line_prev},
1232                            column => $self->{column_prev});                            column => $self->{column_prev});
1233            $self->{state} = BOGUS_COMMENT_STATE;            $self->{state} = BOGUS_COMMENT_STATE;
1234            $self->{current_token} = {type => COMMENT_TOKEN, data => '',            $self->{ct} = {type => COMMENT_TOKEN, data => '',
1235                                      line => $self->{line_prev},                                      line => $self->{line_prev},
1236                                      column => $self->{column_prev},                                      column => $self->{column_prev},
1237                                     };                                     };
1238            ## $self->{next_char} is intentionally left as is            ## $self->{nc} is intentionally left as is
1239            redo A;            redo A;
1240          } else {          } else {
1241            !!!cp (23);            !!!cp (23);
# Line 1065  sub _get_next_token ($) { Line 1256  sub _get_next_token ($) {
1256          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
1257        }        }
1258      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1259          ## NOTE: The "close tag open state" in the spec is implemented as
1260          ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
1261    
1262        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1263        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1264          if (defined $self->{last_emitted_start_tag_name}) {          if (defined $self->{last_stag_name}) {
1265              $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1266            ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>            $self->{s_kwd} = '';
1267            my @next_char;            ## Reconsume.
1268            TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {            redo A;
             push @next_char, $self->{next_char};  
             my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);  
             my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;  
             if ($self->{next_char} == $c or $self->{next_char} == $C) {  
               !!!cp (24);  
               !!!next-input-character;  
               next TAGNAME;  
             } else {  
               !!!cp (25);  
               $self->{next_char} = shift @next_char; # reconsume  
               !!!back-next-input-character (@next_char);  
               $self->{state} = DATA_STATE;  
   
               !!!emit ({type => CHARACTER_TOKEN, data => '</',  
                         line => $l, column => $c,  
                        });  
     
               redo A;  
             }  
           }  
           push @next_char, $self->{next_char};  
         
           unless ($self->{next_char} == 0x0009 or # HT  
                   $self->{next_char} == 0x000A or # LF  
                   $self->{next_char} == 0x000B or # VT  
                   $self->{next_char} == 0x000C or # FF  
                   $self->{next_char} == 0x0020 or # SP  
                   $self->{next_char} == 0x003E or # >  
                   $self->{next_char} == 0x002F or # /  
                   $self->{next_char} == -1) {  
             !!!cp (26);  
             $self->{next_char} = shift @next_char; # reconsume  
             !!!back-next-input-character (@next_char);  
             $self->{state} = DATA_STATE;  
             !!!emit ({type => CHARACTER_TOKEN, data => '</',  
                       line => $l, column => $c,  
                      });  
             redo A;  
           } else {  
             !!!cp (27);  
             $self->{next_char} = shift @next_char;  
             !!!back-next-input-character (@next_char);  
             # and consume...  
           }  
1269          } else {          } else {
1270            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1271              ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1272            !!!cp (28);            !!!cp (28);
           # next-input-character is already done  
1273            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1274              ## Reconsume.
1275            !!!emit ({type => CHARACTER_TOKEN, data => '</',            !!!emit ({type => CHARACTER_TOKEN, data => '</',
1276                      line => $l, column => $c,                      line => $l, column => $c,
1277                     });                     });
1278            redo A;            redo A;
1279          }          }
1280        }        }
1281          
1282        if (0x0041 <= $self->{next_char} and        if (0x0041 <= $self->{nc} and
1283            $self->{next_char} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1284          !!!cp (29);          !!!cp (29);
1285          $self->{current_token}          $self->{ct}
1286              = {type => END_TAG_TOKEN,              = {type => END_TAG_TOKEN,
1287                 tag_name => chr ($self->{next_char} + 0x0020),                 tag_name => chr ($self->{nc} + 0x0020),
1288                 line => $l, column => $c};                 line => $l, column => $c};
1289          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1290          !!!next-input-character;          !!!next-input-character;
1291          redo A;          redo A;
1292        } elsif (0x0061 <= $self->{next_char} and        } elsif (0x0061 <= $self->{nc} and
1293                 $self->{next_char} <= 0x007A) { # a..z                 $self->{nc} <= 0x007A) { # a..z
1294          !!!cp (30);          !!!cp (30);
1295          $self->{current_token} = {type => END_TAG_TOKEN,          $self->{ct} = {type => END_TAG_TOKEN,
1296                                    tag_name => chr ($self->{next_char}),                                    tag_name => chr ($self->{nc}),
1297                                    line => $l, column => $c};                                    line => $l, column => $c};
1298          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1299          !!!next-input-character;          !!!next-input-character;
1300          redo A;          redo A;
1301        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1302          !!!cp (31);          !!!cp (31);
1303          !!!parse-error (type => 'empty end tag',          !!!parse-error (type => 'empty end tag',
1304                          line => $self->{line_prev}, ## "<" in "</>"                          line => $self->{line_prev}, ## "<" in "</>"
# Line 1155  sub _get_next_token ($) { Line 1306  sub _get_next_token ($) {
1306          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1307          !!!next-input-character;          !!!next-input-character;
1308          redo A;          redo A;
1309        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1310          !!!cp (32);          !!!cp (32);
1311          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1312          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
# Line 1170  sub _get_next_token ($) { Line 1321  sub _get_next_token ($) {
1321          !!!cp (33);          !!!cp (33);
1322          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1323          $self->{state} = BOGUS_COMMENT_STATE;          $self->{state} = BOGUS_COMMENT_STATE;
1324          $self->{current_token} = {type => COMMENT_TOKEN, data => '',          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1325                                    line => $self->{line_prev}, # "<" of "</"                                    line => $self->{line_prev}, # "<" of "</"
1326                                    column => $self->{column_prev} - 1,                                    column => $self->{column_prev} - 1,
1327                                   };                                   };
1328          ## $self->{next_char} is intentionally left as is          ## NOTE: $self->{nc} is intentionally left as is.
1329          redo A;          ## Although the "anything else" case of the spec not explicitly
1330            ## states that the next input character is to be reconsumed,
1331            ## it will be included to the |data| of the comment token
1332            ## generated from the bogus end tag, as defined in the
1333            ## "bogus comment state" entry.
1334            redo A;
1335          }
1336        } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1337          my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1338          if (length $ch) {
1339            my $CH = $ch;
1340            $ch =~ tr/a-z/A-Z/;
1341            my $nch = chr $self->{nc};
1342            if ($nch eq $ch or $nch eq $CH) {
1343              !!!cp (24);
1344              ## Stay in the state.
1345              $self->{s_kwd} .= $nch;
1346              !!!next-input-character;
1347              redo A;
1348            } else {
1349              !!!cp (25);
1350              $self->{state} = DATA_STATE;
1351              ## Reconsume.
1352              !!!emit ({type => CHARACTER_TOKEN,
1353                        data => '</' . $self->{s_kwd},
1354                        line => $self->{line_prev},
1355                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1356                       });
1357              redo A;
1358            }
1359          } else { # after "<{tag-name}"
1360            unless ($is_space->{$self->{nc}} or
1361                    {
1362                     0x003E => 1, # >
1363                     0x002F => 1, # /
1364                     -1 => 1, # EOF
1365                    }->{$self->{nc}}) {
1366              !!!cp (26);
1367              ## Reconsume.
1368              $self->{state} = DATA_STATE;
1369              !!!emit ({type => CHARACTER_TOKEN,
1370                        data => '</' . $self->{s_kwd},
1371                        line => $self->{line_prev},
1372                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1373                       });
1374              redo A;
1375            } else {
1376              !!!cp (27);
1377              $self->{ct}
1378                  = {type => END_TAG_TOKEN,
1379                     tag_name => $self->{last_stag_name},
1380                     line => $self->{line_prev},
1381                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1382              $self->{state} = TAG_NAME_STATE;
1383              ## Reconsume.
1384              redo A;
1385            }
1386        }        }
1387      } elsif ($self->{state} == TAG_NAME_STATE) {      } elsif ($self->{state} == TAG_NAME_STATE) {
1388        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1389          !!!cp (34);          !!!cp (34);
1390          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1391          !!!next-input-character;          !!!next-input-character;
1392          redo A;          redo A;
1393        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1394          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1395            !!!cp (35);            !!!cp (35);
1396            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1397          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1398            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1399            #if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1400            #  ## NOTE: This should never be reached.            #  ## NOTE: This should never be reached.
1401            #  !!! cp (36);            #  !!! cp (36);
1402            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 1201  sub _get_next_token ($) { Line 1404  sub _get_next_token ($) {
1404              !!!cp (37);              !!!cp (37);
1405            #}            #}
1406          } else {          } else {
1407            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1408          }          }
1409          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1410          !!!next-input-character;          !!!next-input-character;
1411    
1412          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1413    
1414          redo A;          redo A;
1415        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1416                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1417          !!!cp (38);          !!!cp (38);
1418          $self->{current_token}->{tag_name} .= chr ($self->{next_char} + 0x0020);          $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1419            # start tag or end tag            # start tag or end tag
1420          ## Stay in this state          ## Stay in this state
1421          !!!next-input-character;          !!!next-input-character;
1422          redo A;          redo A;
1423        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1424          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1425          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1426            !!!cp (39);            !!!cp (39);
1427            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1428          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1429            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1430            #if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1431            #  ## NOTE: This state should never be reached.            #  ## NOTE: This state should never be reached.
1432            #  !!! cp (40);            #  !!! cp (40);
1433            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 1232  sub _get_next_token ($) { Line 1435  sub _get_next_token ($) {
1435              !!!cp (41);              !!!cp (41);
1436            #}            #}
1437          } else {          } else {
1438            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1439          }          }
1440          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1441          # reconsume          # reconsume
1442    
1443          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1444    
1445          redo A;          redo A;
1446        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1447          !!!cp (42);          !!!cp (42);
1448          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
1449          !!!next-input-character;          !!!next-input-character;
1450          redo A;          redo A;
1451        } else {        } else {
1452          !!!cp (44);          !!!cp (44);
1453          $self->{current_token}->{tag_name} .= chr $self->{next_char};          $self->{ct}->{tag_name} .= chr $self->{nc};
1454            # start tag or end tag            # start tag or end tag
1455          ## Stay in the state          ## Stay in the state
1456          !!!next-input-character;          !!!next-input-character;
1457          redo A;          redo A;
1458        }        }
1459      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1460        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1461          !!!cp (45);          !!!cp (45);
1462          ## Stay in the state          ## Stay in the state
1463          !!!next-input-character;          !!!next-input-character;
1464          redo A;          redo A;
1465        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1466          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1467            !!!cp (46);            !!!cp (46);
1468            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1469          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1470            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1471            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1472              !!!cp (47);              !!!cp (47);
1473              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1474            } else {            } else {
1475              !!!cp (48);              !!!cp (48);
1476            }            }
1477          } else {          } else {
1478            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1479          }          }
1480          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1481          !!!next-input-character;          !!!next-input-character;
1482    
1483          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1484    
1485          redo A;          redo A;
1486        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1487                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1488          !!!cp (49);          !!!cp (49);
1489          $self->{current_attribute}          $self->{ca}
1490              = {name => chr ($self->{next_char} + 0x0020),              = {name => chr ($self->{nc} + 0x0020),
1491                 value => '',                 value => '',
1492                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1493          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1494          !!!next-input-character;          !!!next-input-character;
1495          redo A;          redo A;
1496        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1497          !!!cp (50);          !!!cp (50);
1498          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
1499          !!!next-input-character;          !!!next-input-character;
1500          redo A;          redo A;
1501        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1502          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1503          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1504            !!!cp (52);            !!!cp (52);
1505            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1506          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1507            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1508            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1509              !!!cp (53);              !!!cp (53);
1510              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1511            } else {            } else {
1512              !!!cp (54);              !!!cp (54);
1513            }            }
1514          } else {          } else {
1515            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1516          }          }
1517          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1518          # reconsume          # reconsume
1519    
1520          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1521    
1522          redo A;          redo A;
1523        } else {        } else {
# Line 1326  sub _get_next_token ($) { Line 1525  sub _get_next_token ($) {
1525               0x0022 => 1, # "               0x0022 => 1, # "
1526               0x0027 => 1, # '               0x0027 => 1, # '
1527               0x003D => 1, # =               0x003D => 1, # =
1528              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1529            !!!cp (55);            !!!cp (55);
1530            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1531          } else {          } else {
1532            !!!cp (56);            !!!cp (56);
1533          }          }
1534          $self->{current_attribute}          $self->{ca}
1535              = {name => chr ($self->{next_char}),              = {name => chr ($self->{nc}),
1536                 value => '',                 value => '',
1537                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1538          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
# Line 1342  sub _get_next_token ($) { Line 1541  sub _get_next_token ($) {
1541        }        }
1542      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1543        my $before_leave = sub {        my $before_leave = sub {
1544          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1545              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1546            !!!cp (57);            !!!cp (57);
1547            !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name}, 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});
1548            ## Discard $self->{current_attribute} # MUST            ## Discard $self->{ca} # MUST
1549          } else {          } else {
1550            !!!cp (58);            !!!cp (58);
1551            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            $self->{ct}->{attributes}->{$self->{ca}->{name}}
1552              = $self->{current_attribute};              = $self->{ca};
1553          }          }
1554        }; # $before_leave        }; # $before_leave
1555    
1556        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1557          !!!cp (59);          !!!cp (59);
1558          $before_leave->();          $before_leave->();
1559          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1560          !!!next-input-character;          !!!next-input-character;
1561          redo A;          redo A;
1562        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1563          !!!cp (60);          !!!cp (60);
1564          $before_leave->();          $before_leave->();
1565          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1566          !!!next-input-character;          !!!next-input-character;
1567          redo A;          redo A;
1568        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1569          $before_leave->();          $before_leave->();
1570          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1571            !!!cp (61);            !!!cp (61);
1572            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1573          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1574            !!!cp (62);            !!!cp (62);
1575            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1576            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1577              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1578            }            }
1579          } else {          } else {
1580            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1581          }          }
1582          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1583          !!!next-input-character;          !!!next-input-character;
1584    
1585          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1586    
1587          redo A;          redo A;
1588        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1589                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1590          !!!cp (63);          !!!cp (63);
1591          $self->{current_attribute}->{name} .= chr ($self->{next_char} + 0x0020);          $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1592          ## Stay in the state          ## Stay in the state
1593          !!!next-input-character;          !!!next-input-character;
1594          redo A;          redo A;
1595        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1596          !!!cp (64);          !!!cp (64);
1597          $before_leave->();          $before_leave->();
1598          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
1599          !!!next-input-character;          !!!next-input-character;
1600          redo A;          redo A;
1601        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1602          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1603          $before_leave->();          $before_leave->();
1604          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1605            !!!cp (66);            !!!cp (66);
1606            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1607          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1608            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1609            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1610              !!!cp (67);              !!!cp (67);
1611              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1612            } else {            } else {
# Line 1419  sub _get_next_token ($) { Line 1614  sub _get_next_token ($) {
1614              !!!cp (68);              !!!cp (68);
1615            }            }
1616          } else {          } else {
1617            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1618          }          }
1619          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1620          # reconsume          # reconsume
1621    
1622          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1623    
1624          redo A;          redo A;
1625        } else {        } else {
1626          if ($self->{next_char} == 0x0022 or # "          if ($self->{nc} == 0x0022 or # "
1627              $self->{next_char} == 0x0027) { # '              $self->{nc} == 0x0027) { # '
1628            !!!cp (69);            !!!cp (69);
1629            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1630          } else {          } else {
1631            !!!cp (70);            !!!cp (70);
1632          }          }
1633          $self->{current_attribute}->{name} .= chr ($self->{next_char});          $self->{ca}->{name} .= chr ($self->{nc});
1634          ## Stay in the state          ## Stay in the state
1635          !!!next-input-character;          !!!next-input-character;
1636          redo A;          redo A;
1637        }        }
1638      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1639        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1640          !!!cp (71);          !!!cp (71);
1641          ## Stay in the state          ## Stay in the state
1642          !!!next-input-character;          !!!next-input-character;
1643          redo A;          redo A;
1644        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1645          !!!cp (72);          !!!cp (72);
1646          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1647          !!!next-input-character;          !!!next-input-character;
1648          redo A;          redo A;
1649        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1650          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1651            !!!cp (73);            !!!cp (73);
1652            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1653          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1654            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1655            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1656              !!!cp (74);              !!!cp (74);
1657              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1658            } else {            } else {
# Line 1469  sub _get_next_token ($) { Line 1660  sub _get_next_token ($) {
1660              !!!cp (75);              !!!cp (75);
1661            }            }
1662          } else {          } else {
1663            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1664          }          }
1665          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1666          !!!next-input-character;          !!!next-input-character;
1667    
1668          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1669    
1670          redo A;          redo A;
1671        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1672                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1673          !!!cp (76);          !!!cp (76);
1674          $self->{current_attribute}          $self->{ca}
1675              = {name => chr ($self->{next_char} + 0x0020),              = {name => chr ($self->{nc} + 0x0020),
1676                 value => '',                 value => '',
1677                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1678          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1679          !!!next-input-character;          !!!next-input-character;
1680          redo A;          redo A;
1681        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1682          !!!cp (77);          !!!cp (77);
1683          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
1684          !!!next-input-character;          !!!next-input-character;
1685          redo A;          redo A;
1686        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1687          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1688          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1689            !!!cp (79);            !!!cp (79);
1690            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1691          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1692            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1693            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1694              !!!cp (80);              !!!cp (80);
1695              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1696            } else {            } else {
# Line 1507  sub _get_next_token ($) { Line 1698  sub _get_next_token ($) {
1698              !!!cp (81);              !!!cp (81);
1699            }            }
1700          } else {          } else {
1701            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1702          }          }
1703          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1704          # reconsume          # reconsume
1705    
1706          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1707    
1708          redo A;          redo A;
1709        } else {        } else {
1710          !!!cp (82);          if ($self->{nc} == 0x0022 or # "
1711          $self->{current_attribute}              $self->{nc} == 0x0027) { # '
1712              = {name => chr ($self->{next_char}),            !!!cp (78);
1713              !!!parse-error (type => 'bad attribute name');
1714            } else {
1715              !!!cp (82);
1716            }
1717            $self->{ca}
1718                = {name => chr ($self->{nc}),
1719                 value => '',                 value => '',
1720                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1721          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
# Line 1526  sub _get_next_token ($) { Line 1723  sub _get_next_token ($) {
1723          redo A;                  redo A;        
1724        }        }
1725      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1726        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP        
1727          !!!cp (83);          !!!cp (83);
1728          ## Stay in the state          ## Stay in the state
1729          !!!next-input-character;          !!!next-input-character;
1730          redo A;          redo A;
1731        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1732          !!!cp (84);          !!!cp (84);
1733          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1734          !!!next-input-character;          !!!next-input-character;
1735          redo A;          redo A;
1736        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1737          !!!cp (85);          !!!cp (85);
1738          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1739          ## reconsume          ## reconsume
1740          redo A;          redo A;
1741        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1742          !!!cp (86);          !!!cp (86);
1743          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1744          !!!next-input-character;          !!!next-input-character;
1745          redo A;          redo A;
1746        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1747          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          !!!parse-error (type => 'empty unquoted attribute value');
1748            if ($self->{ct}->{type} == START_TAG_TOKEN) {
1749            !!!cp (87);            !!!cp (87);
1750            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1751          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1752            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1753            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1754              !!!cp (88);              !!!cp (88);
1755              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1756            } else {            } else {
# Line 1564  sub _get_next_token ($) { Line 1758  sub _get_next_token ($) {
1758              !!!cp (89);              !!!cp (89);
1759            }            }
1760          } else {          } else {
1761            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1762          }          }
1763          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1764          !!!next-input-character;          !!!next-input-character;
1765    
1766          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1767    
1768          redo A;          redo A;
1769        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1770          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1771          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1772            !!!cp (90);            !!!cp (90);
1773            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1774          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1775            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1776            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1777              !!!cp (91);              !!!cp (91);
1778              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1779            } else {            } else {
# Line 1587  sub _get_next_token ($) { Line 1781  sub _get_next_token ($) {
1781              !!!cp (92);              !!!cp (92);
1782            }            }
1783          } else {          } else {
1784            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1785          }          }
1786          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1787          ## reconsume          ## reconsume
1788    
1789          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1790    
1791          redo A;          redo A;
1792        } else {        } else {
1793          if ($self->{next_char} == 0x003D) { # =          if ($self->{nc} == 0x003D) { # =
1794            !!!cp (93);            !!!cp (93);
1795            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1796          } else {          } else {
1797            !!!cp (94);            !!!cp (94);
1798          }          }
1799          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1800          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1801          !!!next-input-character;          !!!next-input-character;
1802          redo A;          redo A;
1803        }        }
1804      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1805        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1806          !!!cp (95);          !!!cp (95);
1807          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1808          !!!next-input-character;          !!!next-input-character;
1809          redo A;          redo A;
1810        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1811          !!!cp (96);          !!!cp (96);
1812          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1813          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1814            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1815            ## implementation of the "consume a character reference" algorithm.
1816            $self->{prev_state} = $self->{state};
1817            $self->{entity_add} = 0x0022; # "
1818            $self->{state} = ENTITY_STATE;
1819          !!!next-input-character;          !!!next-input-character;
1820          redo A;          redo A;
1821        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1822          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1823          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1824            !!!cp (97);            !!!cp (97);
1825            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1826          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1827            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1828            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1829              !!!cp (98);              !!!cp (98);
1830              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1831            } else {            } else {
# Line 1634  sub _get_next_token ($) { Line 1833  sub _get_next_token ($) {
1833              !!!cp (99);              !!!cp (99);
1834            }            }
1835          } else {          } else {
1836            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1837          }          }
1838          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1839          ## reconsume          ## reconsume
1840    
1841          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1842    
1843          redo A;          redo A;
1844        } else {        } else {
1845          !!!cp (100);          !!!cp (100);
1846          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1847            $self->{read_until}->($self->{ca}->{value},
1848                                  q["&],
1849                                  length $self->{ca}->{value});
1850    
1851          ## Stay in the state          ## Stay in the state
1852          !!!next-input-character;          !!!next-input-character;
1853          redo A;          redo A;
1854        }        }
1855      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1856        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1857          !!!cp (101);          !!!cp (101);
1858          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1859          !!!next-input-character;          !!!next-input-character;
1860          redo A;          redo A;
1861        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1862          !!!cp (102);          !!!cp (102);
1863          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1864          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1865            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1866            ## implementation of the "consume a character reference" algorithm.
1867            $self->{entity_add} = 0x0027; # '
1868            $self->{prev_state} = $self->{state};
1869            $self->{state} = ENTITY_STATE;
1870          !!!next-input-character;          !!!next-input-character;
1871          redo A;          redo A;
1872        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1873          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1874          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1875            !!!cp (103);            !!!cp (103);
1876            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1877          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1878            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1879            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1880              !!!cp (104);              !!!cp (104);
1881              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1882            } else {            } else {
# Line 1676  sub _get_next_token ($) { Line 1884  sub _get_next_token ($) {
1884              !!!cp (105);              !!!cp (105);
1885            }            }
1886          } else {          } else {
1887            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1888          }          }
1889          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1890          ## reconsume          ## reconsume
1891    
1892          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1893    
1894          redo A;          redo A;
1895        } else {        } else {
1896          !!!cp (106);          !!!cp (106);
1897          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1898            $self->{read_until}->($self->{ca}->{value},
1899                                  q['&],
1900                                  length $self->{ca}->{value});
1901    
1902          ## Stay in the state          ## Stay in the state
1903          !!!next-input-character;          !!!next-input-character;
1904          redo A;          redo A;
1905        }        }
1906      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1907        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # HT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1908          !!!cp (107);          !!!cp (107);
1909          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1910          !!!next-input-character;          !!!next-input-character;
1911          redo A;          redo A;
1912        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1913          !!!cp (108);          !!!cp (108);
1914          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1915          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1916            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1917            ## implementation of the "consume a character reference" algorithm.
1918            $self->{entity_add} = -1;
1919            $self->{prev_state} = $self->{state};
1920            $self->{state} = ENTITY_STATE;
1921          !!!next-input-character;          !!!next-input-character;
1922          redo A;          redo A;
1923        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1924          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1925            !!!cp (109);            !!!cp (109);
1926            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1927          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1928            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1929            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1930              !!!cp (110);              !!!cp (110);
1931              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1932            } else {            } else {
# Line 1721  sub _get_next_token ($) { Line 1934  sub _get_next_token ($) {
1934              !!!cp (111);              !!!cp (111);
1935            }            }
1936          } else {          } else {
1937            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1938          }          }
1939          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1940          !!!next-input-character;          !!!next-input-character;
1941    
1942          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1943    
1944          redo A;          redo A;
1945        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1946          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1947          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1948            !!!cp (112);            !!!cp (112);
1949            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1950          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1951            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1952            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1953              !!!cp (113);              !!!cp (113);
1954              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1955            } else {            } else {
# Line 1744  sub _get_next_token ($) { Line 1957  sub _get_next_token ($) {
1957              !!!cp (114);              !!!cp (114);
1958            }            }
1959          } else {          } else {
1960            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1961          }          }
1962          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1963          ## reconsume          ## reconsume
1964    
1965          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1966    
1967          redo A;          redo A;
1968        } else {        } else {
# Line 1757  sub _get_next_token ($) { Line 1970  sub _get_next_token ($) {
1970               0x0022 => 1, # "               0x0022 => 1, # "
1971               0x0027 => 1, # '               0x0027 => 1, # '
1972               0x003D => 1, # =               0x003D => 1, # =
1973              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1974            !!!cp (115);            !!!cp (115);
1975            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1976          } else {          } else {
1977            !!!cp (116);            !!!cp (116);
1978          }          }
1979          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1980            $self->{read_until}->($self->{ca}->{value},
1981                                  q["'=& >],
1982                                  length $self->{ca}->{value});
1983    
1984          ## Stay in the state          ## Stay in the state
1985          !!!next-input-character;          !!!next-input-character;
1986          redo A;          redo A;
1987        }        }
     } elsif ($self->{state} == ENTITY_IN_ATTRIBUTE_VALUE_STATE) {  
       my $token = $self->_tokenize_attempt_to_consume_an_entity  
           (1,  
            $self->{last_attribute_value_state}  
              == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE ? 0x0022 : # "  
            $self->{last_attribute_value_state}  
              == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE ? 0x0027 : # '  
            -1);  
   
       unless (defined $token) {  
         !!!cp (117);  
         $self->{current_attribute}->{value} .= '&';  
       } else {  
         !!!cp (118);  
         $self->{current_attribute}->{value} .= $token->{data};  
         $self->{current_attribute}->{has_reference} = $token->{has_reference};  
         ## ISSUE: spec says "append the returned character token to the current attribute's value"  
       }  
   
       $self->{state} = $self->{last_attribute_value_state};  
       # next-input-character is already done  
       redo A;  
1988      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
1989        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1990          !!!cp (118);          !!!cp (118);
1991          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1992          !!!next-input-character;          !!!next-input-character;
1993          redo A;          redo A;
1994        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1995          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1996            !!!cp (119);            !!!cp (119);
1997            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1998          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1999            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2000            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2001              !!!cp (120);              !!!cp (120);
2002              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2003            } else {            } else {
# Line 1814  sub _get_next_token ($) { Line 2005  sub _get_next_token ($) {
2005              !!!cp (121);              !!!cp (121);
2006            }            }
2007          } else {          } else {
2008            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
2009          }          }
2010          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2011          !!!next-input-character;          !!!next-input-character;
2012    
2013          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2014    
2015          redo A;          redo A;
2016        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
2017          !!!cp (122);          !!!cp (122);
2018          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
2019          !!!next-input-character;          !!!next-input-character;
2020          redo A;          redo A;
2021          } elsif ($self->{nc} == -1) {
2022            !!!parse-error (type => 'unclosed tag');
2023            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2024              !!!cp (122.3);
2025              $self->{last_stag_name} = $self->{ct}->{tag_name};
2026            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2027              if ($self->{ct}->{attributes}) {
2028                !!!cp (122.1);
2029                !!!parse-error (type => 'end tag attribute');
2030              } else {
2031                ## NOTE: This state should never be reached.
2032                !!!cp (122.2);
2033              }
2034            } else {
2035              die "$0: $self->{ct}->{type}: Unknown token type";
2036            }
2037            $self->{state} = DATA_STATE;
2038            ## Reconsume.
2039            !!!emit ($self->{ct}); # start tag or end tag
2040            redo A;
2041        } else {        } else {
2042          !!!cp ('124.1');          !!!cp ('124.1');
2043          !!!parse-error (type => 'no space between attributes');          !!!parse-error (type => 'no space between attributes');
# Line 1835  sub _get_next_token ($) { Line 2046  sub _get_next_token ($) {
2046          redo A;          redo A;
2047        }        }
2048      } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {      } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2049        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2050          if ($self->{current_token}->{type} == END_TAG_TOKEN) {          if ($self->{ct}->{type} == END_TAG_TOKEN) {
2051            !!!cp ('124.2');            !!!cp ('124.2');
2052            !!!parse-error (type => 'nestc', token => $self->{current_token});            !!!parse-error (type => 'nestc', token => $self->{ct});
2053            ## TODO: Different type than slash in start tag            ## TODO: Different type than slash in start tag
2054            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2055            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2056              !!!cp ('124.4');              !!!cp ('124.4');
2057              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2058            } else {            } else {
# Line 1856  sub _get_next_token ($) { Line 2067  sub _get_next_token ($) {
2067          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2068          !!!next-input-character;          !!!next-input-character;
2069    
2070          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2071    
2072          redo A;          redo A;
2073          } elsif ($self->{nc} == -1) {
2074            !!!parse-error (type => 'unclosed tag');
2075            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2076              !!!cp (124.7);
2077              $self->{last_stag_name} = $self->{ct}->{tag_name};
2078            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2079              if ($self->{ct}->{attributes}) {
2080                !!!cp (124.5);
2081                !!!parse-error (type => 'end tag attribute');
2082              } else {
2083                ## NOTE: This state should never be reached.
2084                !!!cp (124.6);
2085              }
2086            } else {
2087              die "$0: $self->{ct}->{type}: Unknown token type";
2088            }
2089            $self->{state} = DATA_STATE;
2090            ## Reconsume.
2091            !!!emit ($self->{ct}); # start tag or end tag
2092            redo A;
2093        } else {        } else {
2094          !!!cp ('124.4');          !!!cp ('124.4');
2095          !!!parse-error (type => 'nestc');          !!!parse-error (type => 'nestc');
# Line 1869  sub _get_next_token ($) { Line 2100  sub _get_next_token ($) {
2100        }        }
2101      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2102        ## (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;  
   
           !!!emit ($self->{current_token}); # comment  
2103    
2104            redo A;        ## NOTE: Unlike spec's "bogus comment state", this implementation
2105          } elsif ($self->{next_char} == -1) {        ## consumes characters one-by-one basis.
2106            !!!cp (125);        
2107            $self->{state} = DATA_STATE;        if ($self->{nc} == 0x003E) { # >
2108            ## reconsume          !!!cp (124);
2109            $self->{state} = DATA_STATE;
2110            !!!next-input-character;
2111    
2112            !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2113            redo A;
2114          } elsif ($self->{nc} == -1) {
2115            !!!cp (125);
2116            $self->{state} = DATA_STATE;
2117            ## reconsume
2118    
2119            redo A;          !!!emit ($self->{ct}); # comment
2120          } else {          redo A;
2121            !!!cp (126);        } else {
2122            $self->{current_token}->{data} .= chr ($self->{next_char}); # comment          !!!cp (126);
2123            !!!next-input-character;          $self->{ct}->{data} .= chr ($self->{nc}); # comment
2124            redo BC;          $self->{read_until}->($self->{ct}->{data},
2125          }                                q[>],
2126        } # BC                                length $self->{ct}->{data});
2127    
2128        die "$0: _get_next_token: unexpected case [BC]";          ## Stay in the state.
2129            !!!next-input-character;
2130            redo A;
2131          }
2132      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2133        ## (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};  
2134                
2135        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2136            !!!cp (133);
2137            $self->{state} = MD_HYPHEN_STATE;
2138          !!!next-input-character;          !!!next-input-character;
2139          push @next_char, $self->{next_char};          redo A;
2140          if ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x0044 or # D
2141            !!!cp (127);                 $self->{nc} == 0x0064) { # d
2142            $self->{current_token} = {type => COMMENT_TOKEN, data => '',          ## ASCII case-insensitive.
2143                                      line => $l, column => $c,          !!!cp (130);
2144                                     };          $self->{state} = MD_DOCTYPE_STATE;
2145            $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  
2146          !!!next-input-character;          !!!next-input-character;
2147          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);  
         }  
2148        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2149                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2150                 $self->{next_char} == 0x005B) { # [                 $self->{nc} == 0x005B) { # [
2151            !!!cp (135.4);                
2152            $self->{state} = MD_CDATA_STATE;
2153            $self->{s_kwd} = '[';
2154          !!!next-input-character;          !!!next-input-character;
2155          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);  
         }  
2156        } else {        } else {
2157          !!!cp (136);          !!!cp (136);
2158        }        }
2159    
2160        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment',
2161        $self->{next_char} = shift @next_char;                        line => $self->{line_prev},
2162        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2163          ## Reconsume.
2164        $self->{state} = BOGUS_COMMENT_STATE;        $self->{state} = BOGUS_COMMENT_STATE;
2165        $self->{current_token} = {type => COMMENT_TOKEN, data => '',        $self->{ct} = {type => COMMENT_TOKEN, data => '',
2166                                  line => $l, column => $c,                                  line => $self->{line_prev},
2167                                    column => $self->{column_prev} - 1,
2168                                 };                                 };
2169        redo A;        redo A;
2170              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2171        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2172        ## ISSUE: spec is somewhat unclear on "is the first character that will be in the comment"; what is "that will be in the comment" is what the algorithm defines, isn't it?          !!!cp (127);
2173            $self->{ct} = {type => COMMENT_TOKEN, data => '',
2174                                      line => $self->{line_prev},
2175                                      column => $self->{column_prev} - 2,
2176                                     };
2177            $self->{state} = COMMENT_START_STATE;
2178            !!!next-input-character;
2179            redo A;
2180          } else {
2181            !!!cp (128);
2182            !!!parse-error (type => 'bogus comment',
2183                            line => $self->{line_prev},
2184                            column => $self->{column_prev} - 2);
2185            $self->{state} = BOGUS_COMMENT_STATE;
2186            ## Reconsume.
2187            $self->{ct} = {type => COMMENT_TOKEN,
2188                                      data => '-',
2189                                      line => $self->{line_prev},
2190                                      column => $self->{column_prev} - 2,
2191                                     };
2192            redo A;
2193          }
2194        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2195          ## ASCII case-insensitive.
2196          if ($self->{nc} == [
2197                undef,
2198                0x004F, # O
2199                0x0043, # C
2200                0x0054, # T
2201                0x0059, # Y
2202                0x0050, # P
2203              ]->[length $self->{s_kwd}] or
2204              $self->{nc} == [
2205                undef,
2206                0x006F, # o
2207                0x0063, # c
2208                0x0074, # t
2209                0x0079, # y
2210                0x0070, # p
2211              ]->[length $self->{s_kwd}]) {
2212            !!!cp (131);
2213            ## Stay in the state.
2214            $self->{s_kwd} .= chr $self->{nc};
2215            !!!next-input-character;
2216            redo A;
2217          } elsif ((length $self->{s_kwd}) == 6 and
2218                   ($self->{nc} == 0x0045 or # E
2219                    $self->{nc} == 0x0065)) { # e
2220            !!!cp (129);
2221            $self->{state} = DOCTYPE_STATE;
2222            $self->{ct} = {type => DOCTYPE_TOKEN,
2223                                      quirks => 1,
2224                                      line => $self->{line_prev},
2225                                      column => $self->{column_prev} - 7,
2226                                     };
2227            !!!next-input-character;
2228            redo A;
2229          } else {
2230            !!!cp (132);        
2231            !!!parse-error (type => 'bogus comment',
2232                            line => $self->{line_prev},
2233                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2234            $self->{state} = BOGUS_COMMENT_STATE;
2235            ## Reconsume.
2236            $self->{ct} = {type => COMMENT_TOKEN,
2237                                      data => $self->{s_kwd},
2238                                      line => $self->{line_prev},
2239                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2240                                     };
2241            redo A;
2242          }
2243        } elsif ($self->{state} == MD_CDATA_STATE) {
2244          if ($self->{nc} == {
2245                '[' => 0x0043, # C
2246                '[C' => 0x0044, # D
2247                '[CD' => 0x0041, # A
2248                '[CDA' => 0x0054, # T
2249                '[CDAT' => 0x0041, # A
2250              }->{$self->{s_kwd}}) {
2251            !!!cp (135.1);
2252            ## Stay in the state.
2253            $self->{s_kwd} .= chr $self->{nc};
2254            !!!next-input-character;
2255            redo A;
2256          } elsif ($self->{s_kwd} eq '[CDATA' and
2257                   $self->{nc} == 0x005B) { # [
2258            !!!cp (135.2);
2259            $self->{ct} = {type => CHARACTER_TOKEN,
2260                                      data => '',
2261                                      line => $self->{line_prev},
2262                                      column => $self->{column_prev} - 7};
2263            $self->{state} = CDATA_SECTION_STATE;
2264            !!!next-input-character;
2265            redo A;
2266          } else {
2267            !!!cp (135.3);
2268            !!!parse-error (type => 'bogus comment',
2269                            line => $self->{line_prev},
2270                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2271            $self->{state} = BOGUS_COMMENT_STATE;
2272            ## Reconsume.
2273            $self->{ct} = {type => COMMENT_TOKEN,
2274                                      data => $self->{s_kwd},
2275                                      line => $self->{line_prev},
2276                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2277                                     };
2278            redo A;
2279          }
2280      } elsif ($self->{state} == COMMENT_START_STATE) {      } elsif ($self->{state} == COMMENT_START_STATE) {
2281        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2282          !!!cp (137);          !!!cp (137);
2283          $self->{state} = COMMENT_START_DASH_STATE;          $self->{state} = COMMENT_START_DASH_STATE;
2284          !!!next-input-character;          !!!next-input-character;
2285          redo A;          redo A;
2286        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2287          !!!cp (138);          !!!cp (138);
2288          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2289          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2290          !!!next-input-character;          !!!next-input-character;
2291    
2292          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2293    
2294          redo A;          redo A;
2295        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2296          !!!cp (139);          !!!cp (139);
2297          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2298          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2299          ## reconsume          ## reconsume
2300    
2301          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2302    
2303          redo A;          redo A;
2304        } else {        } else {
2305          !!!cp (140);          !!!cp (140);
2306          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2307              .= chr ($self->{next_char});              .= chr ($self->{nc});
2308          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2309          !!!next-input-character;          !!!next-input-character;
2310          redo A;          redo A;
2311        }        }
2312      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2313        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2314          !!!cp (141);          !!!cp (141);
2315          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2316          !!!next-input-character;          !!!next-input-character;
2317          redo A;          redo A;
2318        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2319          !!!cp (142);          !!!cp (142);
2320          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2321          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2322          !!!next-input-character;          !!!next-input-character;
2323    
2324          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2325    
2326          redo A;          redo A;
2327        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2328          !!!cp (143);          !!!cp (143);
2329          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2330          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2331          ## reconsume          ## reconsume
2332    
2333          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2334    
2335          redo A;          redo A;
2336        } else {        } else {
2337          !!!cp (144);          !!!cp (144);
2338          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2339              .= '-' . chr ($self->{next_char});              .= '-' . chr ($self->{nc});
2340          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2341          !!!next-input-character;          !!!next-input-character;
2342          redo A;          redo A;
2343        }        }
2344      } elsif ($self->{state} == COMMENT_STATE) {      } elsif ($self->{state} == COMMENT_STATE) {
2345        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2346          !!!cp (145);          !!!cp (145);
2347          $self->{state} = COMMENT_END_DASH_STATE;          $self->{state} = COMMENT_END_DASH_STATE;
2348          !!!next-input-character;          !!!next-input-character;
2349          redo A;          redo A;
2350        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2351          !!!cp (146);          !!!cp (146);
2352          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2353          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2354          ## reconsume          ## reconsume
2355    
2356          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2357    
2358          redo A;          redo A;
2359        } else {        } else {
2360          !!!cp (147);          !!!cp (147);
2361          $self->{current_token}->{data} .= chr ($self->{next_char}); # comment          $self->{ct}->{data} .= chr ($self->{nc}); # comment
2362            $self->{read_until}->($self->{ct}->{data},
2363                                  q[-],
2364                                  length $self->{ct}->{data});
2365    
2366          ## Stay in the state          ## Stay in the state
2367          !!!next-input-character;          !!!next-input-character;
2368          redo A;          redo A;
2369        }        }
2370      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2371        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2372          !!!cp (148);          !!!cp (148);
2373          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2374          !!!next-input-character;          !!!next-input-character;
2375          redo A;          redo A;
2376        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2377          !!!cp (149);          !!!cp (149);
2378          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2379          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2380          ## reconsume          ## reconsume
2381    
2382          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2383    
2384          redo A;          redo A;
2385        } else {        } else {
2386          !!!cp (150);          !!!cp (150);
2387          $self->{current_token}->{data} .= '-' . chr ($self->{next_char}); # comment          $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2388          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2389          !!!next-input-character;          !!!next-input-character;
2390          redo A;          redo A;
2391        }        }
2392      } elsif ($self->{state} == COMMENT_END_STATE) {      } elsif ($self->{state} == COMMENT_END_STATE) {
2393        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2394          !!!cp (151);          !!!cp (151);
2395          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2396          !!!next-input-character;          !!!next-input-character;
2397    
2398          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2399    
2400          redo A;          redo A;
2401        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2402          !!!cp (152);          !!!cp (152);
2403          !!!parse-error (type => 'dash in comment',          !!!parse-error (type => 'dash in comment',
2404                          line => $self->{line_prev},                          line => $self->{line_prev},
2405                          column => $self->{column_prev});                          column => $self->{column_prev});
2406          $self->{current_token}->{data} .= '-'; # comment          $self->{ct}->{data} .= '-'; # comment
2407          ## Stay in the state          ## Stay in the state
2408          !!!next-input-character;          !!!next-input-character;
2409          redo A;          redo A;
2410        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2411          !!!cp (153);          !!!cp (153);
2412          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2413          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2414          ## reconsume          ## reconsume
2415    
2416          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2417    
2418          redo A;          redo A;
2419        } else {        } else {
# Line 2172  sub _get_next_token ($) { Line 2421  sub _get_next_token ($) {
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} .= '--' . chr ($self->{next_char}); # comment          $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2425          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2426          !!!next-input-character;          !!!next-input-character;
2427          redo A;          redo A;
2428        }        }
2429      } elsif ($self->{state} == DOCTYPE_STATE) {      } elsif ($self->{state} == DOCTYPE_STATE) {
2430        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2431          !!!cp (155);          !!!cp (155);
2432          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2433          !!!next-input-character;          !!!next-input-character;
# Line 2195  sub _get_next_token ($) { Line 2440  sub _get_next_token ($) {
2440          redo A;          redo A;
2441        }        }
2442      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2443        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2444          !!!cp (157);          !!!cp (157);
2445          ## Stay in the state          ## Stay in the state
2446          !!!next-input-character;          !!!next-input-character;
2447          redo A;          redo A;
2448        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2449          !!!cp (158);          !!!cp (158);
2450          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2451          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2452          !!!next-input-character;          !!!next-input-character;
2453    
2454          !!!emit ($self->{current_token}); # DOCTYPE (quirks)          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2455    
2456          redo A;          redo A;
2457        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2458          !!!cp (159);          !!!cp (159);
2459          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2460          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2461          ## reconsume          ## reconsume
2462    
2463          !!!emit ($self->{current_token}); # DOCTYPE (quirks)          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2464    
2465          redo A;          redo A;
2466        } else {        } else {
2467          !!!cp (160);          !!!cp (160);
2468          $self->{current_token}->{name} = chr $self->{next_char};          $self->{ct}->{name} = chr $self->{nc};
2469          delete $self->{current_token}->{quirks};          delete $self->{ct}->{quirks};
2470  ## ISSUE: "Set the token's name name to the" in the spec  ## ISSUE: "Set the token's name name to the" in the spec
2471          $self->{state} = DOCTYPE_NAME_STATE;          $self->{state} = DOCTYPE_NAME_STATE;
2472          !!!next-input-character;          !!!next-input-character;
# Line 2233  sub _get_next_token ($) { Line 2474  sub _get_next_token ($) {
2474        }        }
2475      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2476  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2477        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2478          !!!cp (161);          !!!cp (161);
2479          $self->{state} = AFTER_DOCTYPE_NAME_STATE;          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
2480          !!!next-input-character;          !!!next-input-character;
2481          redo A;          redo A;
2482        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2483          !!!cp (162);          !!!cp (162);
2484          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2485          !!!next-input-character;          !!!next-input-character;
2486    
2487          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2488    
2489          redo A;          redo A;
2490        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2491          !!!cp (163);          !!!cp (163);
2492          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2493          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2494          ## reconsume          ## reconsume
2495    
2496          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2497          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2498    
2499          redo A;          redo A;
2500        } else {        } else {
2501          !!!cp (164);          !!!cp (164);
2502          $self->{current_token}->{name}          $self->{ct}->{name}
2503            .= chr ($self->{next_char}); # DOCTYPE            .= chr ($self->{nc}); # DOCTYPE
2504          ## Stay in the state          ## Stay in the state
2505          !!!next-input-character;          !!!next-input-character;
2506          redo A;          redo A;
2507        }        }
2508      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2509        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2510          !!!cp (165);          !!!cp (165);
2511          ## Stay in the state          ## Stay in the state
2512          !!!next-input-character;          !!!next-input-character;
2513          redo A;          redo A;
2514        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2515          !!!cp (166);          !!!cp (166);
2516          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2517          !!!next-input-character;          !!!next-input-character;
2518    
2519          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2520    
2521          redo A;          redo A;
2522        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2523          !!!cp (167);          !!!cp (167);
2524          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2525          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2526          ## reconsume          ## reconsume
2527    
2528          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2529          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2530    
2531          redo A;          redo A;
2532        } elsif ($self->{next_char} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2533                 $self->{next_char} == 0x0070) { # p                 $self->{nc} == 0x0070) { # p
2534            $self->{state} = PUBLIC_STATE;
2535            $self->{s_kwd} = chr $self->{nc};
2536          !!!next-input-character;          !!!next-input-character;
2537          if ($self->{next_char} == 0x0055 or # U          redo A;
2538              $self->{next_char} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2539            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2540            if ($self->{next_char} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2541                $self->{next_char} == 0x0062) { # b          $self->{s_kwd} = chr $self->{nc};
             !!!next-input-character;  
             if ($self->{next_char} == 0x004C or # L  
                 $self->{next_char} == 0x006C) { # l  
               !!!next-input-character;  
               if ($self->{next_char} == 0x0049 or # I  
                   $self->{next_char} == 0x0069) { # i  
                 !!!next-input-character;  
                 if ($self->{next_char} == 0x0043 or # C  
                     $self->{next_char} == 0x0063) { # c  
                   !!!cp (168);  
                   $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
                   !!!next-input-character;  
                   redo A;  
                 } else {  
                   !!!cp (169);  
                 }  
               } else {  
                 !!!cp (170);  
               }  
             } else {  
               !!!cp (171);  
             }  
           } else {  
             !!!cp (172);  
           }  
         } else {  
           !!!cp (173);  
         }  
   
         #  
       } elsif ($self->{next_char} == 0x0053 or # S  
                $self->{next_char} == 0x0073) { # s  
2542          !!!next-input-character;          !!!next-input-character;
2543          if ($self->{next_char} == 0x0059 or # Y          redo A;
             $self->{next_char} == 0x0079) { # y  
           !!!next-input-character;  
           if ($self->{next_char} == 0x0053 or # S  
               $self->{next_char} == 0x0073) { # s  
             !!!next-input-character;  
             if ($self->{next_char} == 0x0054 or # T  
                 $self->{next_char} == 0x0074) { # t  
               !!!next-input-character;  
               if ($self->{next_char} == 0x0045 or # E  
                   $self->{next_char} == 0x0065) { # e  
                 !!!next-input-character;  
                 if ($self->{next_char} == 0x004D or # M  
                     $self->{next_char} == 0x006D) { # m  
                   !!!cp (174);  
                   $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
                   !!!next-input-character;  
                   redo A;  
                 } else {  
                   !!!cp (175);  
                 }  
               } else {  
                 !!!cp (176);  
               }  
             } else {  
               !!!cp (177);  
             }  
           } else {  
             !!!cp (178);  
           }  
         } else {  
           !!!cp (179);  
         }  
   
         #  
2544        } else {        } else {
2545          !!!cp (180);          !!!cp (180);
2546            !!!parse-error (type => 'string after DOCTYPE name');
2547            $self->{ct}->{quirks} = 1;
2548    
2549            $self->{state} = BOGUS_DOCTYPE_STATE;
2550          !!!next-input-character;          !!!next-input-character;
2551          #          redo A;
2552        }        }
2553        } elsif ($self->{state} == PUBLIC_STATE) {
2554          ## ASCII case-insensitive
2555          if ($self->{nc} == [
2556                undef,
2557                0x0055, # U
2558                0x0042, # B
2559                0x004C, # L
2560                0x0049, # I
2561              ]->[length $self->{s_kwd}] or
2562              $self->{nc} == [
2563                undef,
2564                0x0075, # u
2565                0x0062, # b
2566                0x006C, # l
2567                0x0069, # i
2568              ]->[length $self->{s_kwd}]) {
2569            !!!cp (175);
2570            ## Stay in the state.
2571            $self->{s_kwd} .= chr $self->{nc};
2572            !!!next-input-character;
2573            redo A;
2574          } elsif ((length $self->{s_kwd}) == 5 and
2575                   ($self->{nc} == 0x0043 or # C
2576                    $self->{nc} == 0x0063)) { # c
2577            !!!cp (168);
2578            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2579            !!!next-input-character;
2580            redo A;
2581          } else {
2582            !!!cp (169);
2583            !!!parse-error (type => 'string after DOCTYPE name',
2584                            line => $self->{line_prev},
2585                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2586            $self->{ct}->{quirks} = 1;
2587    
2588        !!!parse-error (type => 'string after DOCTYPE name');          $self->{state} = BOGUS_DOCTYPE_STATE;
2589        $self->{current_token}->{quirks} = 1;          ## Reconsume.
2590            redo A;
2591          }
2592        } elsif ($self->{state} == SYSTEM_STATE) {
2593          ## ASCII case-insensitive
2594          if ($self->{nc} == [
2595                undef,
2596                0x0059, # Y
2597                0x0053, # S
2598                0x0054, # T
2599                0x0045, # E
2600              ]->[length $self->{s_kwd}] or
2601              $self->{nc} == [
2602                undef,
2603                0x0079, # y
2604                0x0073, # s
2605                0x0074, # t
2606                0x0065, # e
2607              ]->[length $self->{s_kwd}]) {
2608            !!!cp (170);
2609            ## Stay in the state.
2610            $self->{s_kwd} .= chr $self->{nc};
2611            !!!next-input-character;
2612            redo A;
2613          } elsif ((length $self->{s_kwd}) == 5 and
2614                   ($self->{nc} == 0x004D or # M
2615                    $self->{nc} == 0x006D)) { # m
2616            !!!cp (171);
2617            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2618            !!!next-input-character;
2619            redo A;
2620          } else {
2621            !!!cp (172);
2622            !!!parse-error (type => 'string after DOCTYPE name',
2623                            line => $self->{line_prev},
2624                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2625            $self->{ct}->{quirks} = 1;
2626    
2627        $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2628        # next-input-character is already done          ## Reconsume.
2629        redo A;          redo A;
2630          }
2631      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2632        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2633          !!!cp (181);          !!!cp (181);
2634          ## Stay in the state          ## Stay in the state
2635          !!!next-input-character;          !!!next-input-character;
2636          redo A;          redo A;
2637        } elsif ($self->{next_char} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2638          !!!cp (182);          !!!cp (182);
2639          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2640          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2641          !!!next-input-character;          !!!next-input-character;
2642          redo A;          redo A;
2643        } elsif ($self->{next_char} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2644          !!!cp (183);          !!!cp (183);
2645          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2646          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2647          !!!next-input-character;          !!!next-input-character;
2648          redo A;          redo A;
2649        } elsif ($self->{next_char} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2650          !!!cp (184);          !!!cp (184);
2651          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2652    
2653          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2654          !!!next-input-character;          !!!next-input-character;
2655    
2656          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2657          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2658    
2659          redo A;          redo A;
2660        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2661          !!!cp (185);          !!!cp (185);
2662          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2663    
2664          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2665          ## reconsume          ## reconsume
2666    
2667          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2668          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2669    
2670          redo A;          redo A;
2671        } else {        } else {
2672          !!!cp (186);          !!!cp (186);
2673          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2674          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2675    
2676          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2677          !!!next-input-character;          !!!next-input-character;
2678          redo A;          redo A;
2679        }        }
2680      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2681        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2682          !!!cp (187);          !!!cp (187);
2683          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2684          !!!next-input-character;          !!!next-input-character;
2685          redo A;          redo A;
2686        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2687          !!!cp (188);          !!!cp (188);
2688          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2689    
2690          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2691          !!!next-input-character;          !!!next-input-character;
2692    
2693          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2694          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2695    
2696          redo A;          redo A;
2697        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2698          !!!cp (189);          !!!cp (189);
2699          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2700    
2701          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2702          ## reconsume          ## reconsume
2703    
2704          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2705          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2706    
2707          redo A;          redo A;
2708        } else {        } else {
2709          !!!cp (190);          !!!cp (190);
2710          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2711              .= chr $self->{next_char};              .= chr $self->{nc};
2712            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2713                                  length $self->{ct}->{pubid});
2714    
2715          ## Stay in the state          ## Stay in the state
2716          !!!next-input-character;          !!!next-input-character;
2717          redo A;          redo A;
2718        }        }
2719      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2720        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2721          !!!cp (191);          !!!cp (191);
2722          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2723          !!!next-input-character;          !!!next-input-character;
2724          redo A;          redo A;
2725        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2726          !!!cp (192);          !!!cp (192);
2727          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2728    
2729          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2730          !!!next-input-character;          !!!next-input-character;
2731    
2732          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2733          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2734    
2735          redo A;          redo A;
2736        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2737          !!!cp (193);          !!!cp (193);
2738          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2739    
2740          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2741          ## reconsume          ## reconsume
2742    
2743          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2744          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2745    
2746          redo A;          redo A;
2747        } else {        } else {
2748          !!!cp (194);          !!!cp (194);
2749          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2750              .= chr $self->{next_char};              .= chr $self->{nc};
2751            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2752                                  length $self->{ct}->{pubid});
2753    
2754          ## Stay in the state          ## Stay in the state
2755          !!!next-input-character;          !!!next-input-character;
2756          redo A;          redo A;
2757        }        }
2758      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2759        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2760          !!!cp (195);          !!!cp (195);
2761          ## Stay in the state          ## Stay in the state
2762          !!!next-input-character;          !!!next-input-character;
2763          redo A;          redo A;
2764        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2765          !!!cp (196);          !!!cp (196);
2766          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2767          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2768          !!!next-input-character;          !!!next-input-character;
2769          redo A;          redo A;
2770        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2771          !!!cp (197);          !!!cp (197);
2772          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2773          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2774          !!!next-input-character;          !!!next-input-character;
2775          redo A;          redo A;
2776        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2777          !!!cp (198);          !!!cp (198);
2778          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2779          !!!next-input-character;          !!!next-input-character;
2780    
2781          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2782    
2783          redo A;          redo A;
2784        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2785          !!!cp (199);          !!!cp (199);
2786          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2787    
2788          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2789          ## reconsume          ## reconsume
2790    
2791          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2792          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2793    
2794          redo A;          redo A;
2795        } else {        } else {
2796          !!!cp (200);          !!!cp (200);
2797          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2798          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2799    
2800          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2801          !!!next-input-character;          !!!next-input-character;
2802          redo A;          redo A;
2803        }        }
2804      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2805        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2806          !!!cp (201);          !!!cp (201);
2807          ## Stay in the state          ## Stay in the state
2808          !!!next-input-character;          !!!next-input-character;
2809          redo A;          redo A;
2810        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2811          !!!cp (202);          !!!cp (202);
2812          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2813          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2814          !!!next-input-character;          !!!next-input-character;
2815          redo A;          redo A;
2816        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2817          !!!cp (203);          !!!cp (203);
2818          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2819          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2820          !!!next-input-character;          !!!next-input-character;
2821          redo A;          redo A;
2822        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2823          !!!cp (204);          !!!cp (204);
2824          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2825          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2826          !!!next-input-character;          !!!next-input-character;
2827    
2828          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2829          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2830    
2831          redo A;          redo A;
2832        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2833          !!!cp (205);          !!!cp (205);
2834          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2835    
2836          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2837          ## reconsume          ## reconsume
2838    
2839          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2840          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2841    
2842          redo A;          redo A;
2843        } else {        } else {
2844          !!!cp (206);          !!!cp (206);
2845          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2846          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2847    
2848          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2849          !!!next-input-character;          !!!next-input-character;
2850          redo A;          redo A;
2851        }        }
2852      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2853        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2854          !!!cp (207);          !!!cp (207);
2855          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2856          !!!next-input-character;          !!!next-input-character;
2857          redo A;          redo A;
2858        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2859          !!!cp (208);          !!!cp (208);
2860          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2861    
2862          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2863          !!!next-input-character;          !!!next-input-character;
2864    
2865          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2866          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2867    
2868          redo A;          redo A;
2869        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2870          !!!cp (209);          !!!cp (209);
2871          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2872    
2873          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2874          ## reconsume          ## reconsume
2875    
2876          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2877          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2878    
2879          redo A;          redo A;
2880        } else {        } else {
2881          !!!cp (210);          !!!cp (210);
2882          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2883              .= chr $self->{next_char};              .= chr $self->{nc};
2884            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2885                                  length $self->{ct}->{sysid});
2886    
2887          ## Stay in the state          ## Stay in the state
2888          !!!next-input-character;          !!!next-input-character;
2889          redo A;          redo A;
2890        }        }
2891      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2892        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2893          !!!cp (211);          !!!cp (211);
2894          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2895          !!!next-input-character;          !!!next-input-character;
2896          redo A;          redo A;
2897        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2898          !!!cp (212);          !!!cp (212);
2899          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2900    
2901          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2902          !!!next-input-character;          !!!next-input-character;
2903    
2904          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2905          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2906    
2907          redo A;          redo A;
2908        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2909          !!!cp (213);          !!!cp (213);
2910          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2911    
2912          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2913          ## reconsume          ## reconsume
2914    
2915          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2916          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2917    
2918          redo A;          redo A;
2919        } else {        } else {
2920          !!!cp (214);          !!!cp (214);
2921          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2922              .= chr $self->{next_char};              .= chr $self->{nc};
2923            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2924                                  length $self->{ct}->{sysid});
2925    
2926          ## Stay in the state          ## Stay in the state
2927          !!!next-input-character;          !!!next-input-character;
2928          redo A;          redo A;
2929        }        }
2930      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2931        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2932          !!!cp (215);          !!!cp (215);
2933          ## Stay in the state          ## Stay in the state
2934          !!!next-input-character;          !!!next-input-character;
2935          redo A;          redo A;
2936        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2937          !!!cp (216);          !!!cp (216);
2938          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2939          !!!next-input-character;          !!!next-input-character;
2940    
2941          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2942    
2943          redo A;          redo A;
2944        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2945          !!!cp (217);          !!!cp (217);
2946          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
   
2947          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2948          ## reconsume          ## reconsume
2949    
2950          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2951          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2952    
2953          redo A;          redo A;
2954        } else {        } else {
2955          !!!cp (218);          !!!cp (218);
2956          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2957          #$self->{current_token}->{quirks} = 1;          #$self->{ct}->{quirks} = 1;
2958    
2959          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2960          !!!next-input-character;          !!!next-input-character;
2961          redo A;          redo A;
2962        }        }
2963      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2964        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2965          !!!cp (219);          !!!cp (219);
2966          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2967          !!!next-input-character;          !!!next-input-character;
2968    
2969          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2970    
2971          redo A;          redo A;
2972        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2973          !!!cp (220);          !!!cp (220);
2974          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2975          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2976          ## reconsume          ## reconsume
2977    
2978          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2979    
2980          redo A;          redo A;
2981        } else {        } else {
2982          !!!cp (221);          !!!cp (221);
2983            my $s = '';
2984            $self->{read_until}->($s, q[>], 0);
2985    
2986          ## Stay in the state          ## Stay in the state
2987          !!!next-input-character;          !!!next-input-character;
2988          redo A;          redo A;
2989        }        }
2990      } elsif ($self->{state} == CDATA_BLOCK_STATE) {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
2991        my $s = '';        ## NOTE: "CDATA section state" in the state is jointly implemented
2992          ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
2993          ## and |CDATA_SECTION_MSE2_STATE|.
2994                
2995        my ($l, $c) = ($self->{line}, $self->{column});        if ($self->{nc} == 0x005D) { # ]
2996            !!!cp (221.1);
2997        CS: while ($self->{next_char} != -1) {          $self->{state} = CDATA_SECTION_MSE1_STATE;
2998          if ($self->{next_char} == 0x005D) { # ]          !!!next-input-character;
2999            !!!next-input-character;          redo A;
3000            if ($self->{next_char} == 0x005D) { # ]        } elsif ($self->{nc} == -1) {
3001              !!!next-input-character;          $self->{state} = DATA_STATE;
             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};  
3002          !!!next-input-character;          !!!next-input-character;
3003        } # CS          if (length $self->{ct}->{data}) { # character
3004              !!!cp (221.2);
3005              !!!emit ($self->{ct}); # character
3006            } else {
3007              !!!cp (221.3);
3008              ## No token to emit. $self->{ct} is discarded.
3009            }        
3010            redo A;
3011          } else {
3012            !!!cp (221.4);
3013            $self->{ct}->{data} .= chr $self->{nc};
3014            $self->{read_until}->($self->{ct}->{data},
3015                                  q<]>,
3016                                  length $self->{ct}->{data});
3017    
3018        $self->{state} = DATA_STATE;          ## Stay in the state.
3019        ## next-input-character done or EOF, which is reconsumed.          !!!next-input-character;
3020            redo A;
3021          }
3022    
3023        if (length $s) {        ## ISSUE: "text tokens" in spec.
3024        } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3025          if ($self->{nc} == 0x005D) { # ]
3026            !!!cp (221.5);
3027            $self->{state} = CDATA_SECTION_MSE2_STATE;
3028            !!!next-input-character;
3029            redo A;
3030          } else {
3031          !!!cp (221.6);          !!!cp (221.6);
3032          !!!emit ({type => CHARACTER_TOKEN, data => $s,          $self->{ct}->{data} .= ']';
3033                    line => $l, column => $c});          $self->{state} = CDATA_SECTION_STATE;
3034            ## Reconsume.
3035            redo A;
3036          }
3037        } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3038          if ($self->{nc} == 0x003E) { # >
3039            $self->{state} = DATA_STATE;
3040            !!!next-input-character;
3041            if (length $self->{ct}->{data}) { # character
3042              !!!cp (221.7);
3043              !!!emit ($self->{ct}); # character
3044            } else {
3045              !!!cp (221.8);
3046              ## No token to emit. $self->{ct} is discarded.
3047            }
3048            redo A;
3049          } elsif ($self->{nc} == 0x005D) { # ]
3050            !!!cp (221.9); # character
3051            $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3052            ## Stay in the state.
3053            !!!next-input-character;
3054            redo A;
3055        } else {        } else {
3056          !!!cp (221.7);          !!!cp (221.11);
3057            $self->{ct}->{data} .= ']]'; # character
3058            $self->{state} = CDATA_SECTION_STATE;
3059            ## Reconsume.
3060            redo A;
3061          }
3062        } elsif ($self->{state} == ENTITY_STATE) {
3063          if ($is_space->{$self->{nc}} or
3064              {
3065                0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3066                $self->{entity_add} => 1,
3067              }->{$self->{nc}}) {
3068            !!!cp (1001);
3069            ## Don't consume
3070            ## No error
3071            ## Return nothing.
3072            #
3073          } elsif ($self->{nc} == 0x0023) { # #
3074            !!!cp (999);
3075            $self->{state} = ENTITY_HASH_STATE;
3076            $self->{s_kwd} = '#';
3077            !!!next-input-character;
3078            redo A;
3079          } elsif ((0x0041 <= $self->{nc} and
3080                    $self->{nc} <= 0x005A) or # A..Z
3081                   (0x0061 <= $self->{nc} and
3082                    $self->{nc} <= 0x007A)) { # a..z
3083            !!!cp (998);
3084            require Whatpm::_NamedEntityList;
3085            $self->{state} = ENTITY_NAME_STATE;
3086            $self->{s_kwd} = chr $self->{nc};
3087            $self->{entity__value} = $self->{s_kwd};
3088            $self->{entity__match} = 0;
3089            !!!next-input-character;
3090            redo A;
3091          } else {
3092            !!!cp (1027);
3093            !!!parse-error (type => 'bare ero');
3094            ## Return nothing.
3095            #
3096        }        }
3097    
3098        redo A;        ## NOTE: No character is consumed by the "consume a character
3099          ## reference" algorithm.  In other word, there is an "&" character
3100        ## ISSUE: "text tokens" in spec.        ## that does not introduce a character reference, which would be
3101        ## TODO: Streaming support        ## appended to the parent element or the attribute value in later
3102      } else {        ## process of the tokenizer.
3103        die "$0: $self->{state}: Unknown state";  
3104      }        if ($self->{prev_state} == DATA_STATE) {
3105    } # A            !!!cp (997);
3106            $self->{state} = $self->{prev_state};
3107    die "$0: _get_next_token: unexpected case";          ## Reconsume.
3108  } # _get_next_token          !!!emit ({type => CHARACTER_TOKEN, data => '&',
3109                      line => $self->{line_prev},
3110  sub _tokenize_attempt_to_consume_an_entity ($$$) {                    column => $self->{column_prev},
3111    my ($self, $in_attr, $additional) = @_;                   });
3112            redo A;
3113    my ($l, $c) = ($self->{line_prev}, $self->{column_prev});        } else {
3114            !!!cp (996);
3115            $self->{ca}->{value} .= '&';
3116            $self->{state} = $self->{prev_state};
3117            ## Reconsume.
3118            redo A;
3119          }
3120        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3121          if ($self->{nc} == 0x0078 or # x
3122              $self->{nc} == 0x0058) { # X
3123            !!!cp (995);
3124            $self->{state} = HEXREF_X_STATE;
3125            $self->{s_kwd} .= chr $self->{nc};
3126            !!!next-input-character;
3127            redo A;
3128          } elsif (0x0030 <= $self->{nc} and
3129                   $self->{nc} <= 0x0039) { # 0..9
3130            !!!cp (994);
3131            $self->{state} = NCR_NUM_STATE;
3132            $self->{s_kwd} = $self->{nc} - 0x0030;
3133            !!!next-input-character;
3134            redo A;
3135          } else {
3136            !!!parse-error (type => 'bare nero',
3137                            line => $self->{line_prev},
3138                            column => $self->{column_prev} - 1);
3139    
3140    if ({          ## NOTE: According to the spec algorithm, nothing is returned,
3141         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,          ## and then "&#" is appended to the parent element or the attribute
3142         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR          ## value in the later processing.
3143         $additional => 1,  
3144        }->{$self->{next_char}}) {          if ($self->{prev_state} == DATA_STATE) {
3145      !!!cp (1001);            !!!cp (1019);
3146      ## Don't consume            $self->{state} = $self->{prev_state};
3147      ## No error            ## Reconsume.
3148      return undef;            !!!emit ({type => CHARACTER_TOKEN,
3149    } elsif ($self->{next_char} == 0x0023) { # #                      data => '&#',
3150      !!!next-input-character;                      line => $self->{line_prev},
3151      if ($self->{next_char} == 0x0078 or # x                      column => $self->{column_prev} - 1,
3152          $self->{next_char} == 0x0058) { # X                     });
3153        my $code;            redo A;
       X: {  
         my $x_char = $self->{next_char};  
         !!!next-input-character;  
         if (0x0030 <= $self->{next_char} and  
             $self->{next_char} <= 0x0039) { # 0..9  
           !!!cp (1002);  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_char} - 0x0030;  
           redo X;  
         } elsif (0x0061 <= $self->{next_char} and  
                  $self->{next_char} <= 0x0066) { # a..f  
           !!!cp (1003);  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_char} - 0x0060 + 9;  
           redo X;  
         } elsif (0x0041 <= $self->{next_char} and  
                  $self->{next_char} <= 0x0046) { # A..F  
           !!!cp (1004);  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_char} - 0x0040 + 9;  
           redo X;  
         } elsif (not defined $code) { # no hexadecimal digit  
           !!!cp (1005);  
           !!!parse-error (type => 'bare hcro', line => $l, column => $c);  
           !!!back-next-input-character ($x_char, $self->{next_char});  
           $self->{next_char} = 0x0023; # #  
           return undef;  
         } elsif ($self->{next_char} == 0x003B) { # ;  
           !!!cp (1006);  
           !!!next-input-character;  
3154          } else {          } else {
3155            !!!cp (1007);            !!!cp (993);
3156            !!!parse-error (type => 'no refc', line => $l, column => $c);            $self->{ca}->{value} .= '&#';
3157              $self->{state} = $self->{prev_state};
3158              ## Reconsume.
3159              redo A;
3160          }          }
3161          }
3162          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {      } elsif ($self->{state} == NCR_NUM_STATE) {
3163            !!!cp (1008);        if (0x0030 <= $self->{nc} and
3164            !!!parse-error (type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c);            $self->{nc} <= 0x0039) { # 0..9
           $code = 0xFFFD;  
         } elsif ($code > 0x10FFFF) {  
           !!!cp (1009);  
           !!!parse-error (type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c);  
           $code = 0xFFFD;  
         } elsif ($code == 0x000D) {  
           !!!cp (1010);  
           !!!parse-error (type => 'CR character reference', line => $l, column => $c);  
           $code = 0x000A;  
         } elsif (0x80 <= $code and $code <= 0x9F) {  
           !!!cp (1011);  
           !!!parse-error (type => (sprintf 'C1 character reference:U+%04X', $code), line => $l, column => $c);  
           $code = $c1_entity_char->{$code};  
         }  
   
         return {type => CHARACTER_TOKEN, data => chr $code,  
                 has_reference => 1,  
                 line => $l, column => $c,  
                };  
       } # X  
     } elsif (0x0030 <= $self->{next_char} and  
              $self->{next_char} <= 0x0039) { # 0..9  
       my $code = $self->{next_char} - 0x0030;  
       !!!next-input-character;  
         
       while (0x0030 <= $self->{next_char} and  
                 $self->{next_char} <= 0x0039) { # 0..9  
3165          !!!cp (1012);          !!!cp (1012);
3166          $code *= 10;          $self->{s_kwd} *= 10;
3167          $code += $self->{next_char} - 0x0030;          $self->{s_kwd} += $self->{nc} - 0x0030;
3168                    
3169            ## Stay in the state.
3170          !!!next-input-character;          !!!next-input-character;
3171        }          redo A;
3172          } elsif ($self->{nc} == 0x003B) { # ;
       if ($self->{next_char} == 0x003B) { # ;  
3173          !!!cp (1013);          !!!cp (1013);
3174          !!!next-input-character;          !!!next-input-character;
3175            #
3176        } else {        } else {
3177          !!!cp (1014);          !!!cp (1014);
3178          !!!parse-error (type => 'no refc', line => $l, column => $c);          !!!parse-error (type => 'no refc');
3179            ## Reconsume.
3180            #
3181        }        }
3182    
3183        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3184          my $l = $self->{line_prev};
3185          my $c = $self->{column_prev};
3186          if ($charref_map->{$code}) {
3187          !!!cp (1015);          !!!cp (1015);
3188          !!!parse-error (type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c);          !!!parse-error (type => 'invalid character reference',
3189          $code = 0xFFFD;                          text => (sprintf 'U+%04X', $code),
3190                            line => $l, column => $c);
3191            $code = $charref_map->{$code};
3192        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3193          !!!cp (1016);          !!!cp (1016);
3194          !!!parse-error (type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c);          !!!parse-error (type => 'invalid character reference',
3195                            text => (sprintf 'U-%08X', $code),
3196                            line => $l, column => $c);
3197          $code = 0xFFFD;          $code = 0xFFFD;
       } elsif ($code == 0x000D) {  
         !!!cp (1017);  
         !!!parse-error (type => 'CR character reference', line => $l, column => $c);  
         $code = 0x000A;  
       } elsif (0x80 <= $code and $code <= 0x9F) {  
         !!!cp (1018);  
         !!!parse-error (type => (sprintf 'C1 character reference:U+%04X', $code), line => $l, column => $c);  
         $code = $c1_entity_char->{$code};  
3198        }        }
3199          
3200        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1,        if ($self->{prev_state} == DATA_STATE) {
3201                line => $l, column => $c,          !!!cp (992);
3202               };          $self->{state} = $self->{prev_state};
3203      } else {          ## Reconsume.
3204        !!!cp (1019);          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3205        !!!parse-error (type => 'bare nero', line => $l, column => $c);                    line => $l, column => $c,
3206        !!!back-next-input-character ($self->{next_char});                   });
3207        $self->{next_char} = 0x0023; # #          redo A;
3208        return undef;        } else {
3209      }          !!!cp (991);
3210    } elsif ((0x0041 <= $self->{next_char} and          $self->{ca}->{value} .= chr $code;
3211              $self->{next_char} <= 0x005A) or          $self->{ca}->{has_reference} = 1;
3212             (0x0061 <= $self->{next_char} and          $self->{state} = $self->{prev_state};
3213              $self->{next_char} <= 0x007A)) {          ## Reconsume.
3214      my $entity_name = chr $self->{next_char};          redo A;
3215      !!!next-input-character;        }
3216        } elsif ($self->{state} == HEXREF_X_STATE) {
3217      my $value = $entity_name;        if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3218      my $match = 0;            (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3219      require Whatpm::_NamedEntityList;            (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3220      our $EntityChar;          # 0..9, A..F, a..f
3221            !!!cp (990);
3222      while (length $entity_name < 30 and          $self->{state} = HEXREF_HEX_STATE;
3223             ## NOTE: Some number greater than the maximum length of entity name          $self->{s_kwd} = 0;
3224             ((0x0041 <= $self->{next_char} and # a          ## Reconsume.
3225               $self->{next_char} <= 0x005A) or # x          redo A;
3226              (0x0061 <= $self->{next_char} and # a        } else {
3227               $self->{next_char} <= 0x007A) or # z          !!!parse-error (type => 'bare hcro',
3228              (0x0030 <= $self->{next_char} and # 0                          line => $self->{line_prev},
3229               $self->{next_char} <= 0x0039) or # 9                          column => $self->{column_prev} - 2);
3230              $self->{next_char} == 0x003B)) { # ;  
3231        $entity_name .= chr $self->{next_char};          ## NOTE: According to the spec algorithm, nothing is returned,
3232        if (defined $EntityChar->{$entity_name}) {          ## and then "&#" followed by "X" or "x" is appended to the parent
3233          if ($self->{next_char} == 0x003B) { # ;          ## element or the attribute value in the later processing.
3234            !!!cp (1020);  
3235            $value = $EntityChar->{$entity_name};          if ($self->{prev_state} == DATA_STATE) {
3236            $match = 1;            !!!cp (1005);
3237            !!!next-input-character;            $self->{state} = $self->{prev_state};
3238            last;            ## Reconsume.
3239              !!!emit ({type => CHARACTER_TOKEN,
3240                        data => '&' . $self->{s_kwd},
3241                        line => $self->{line_prev},
3242                        column => $self->{column_prev} - length $self->{s_kwd},
3243                       });
3244              redo A;
3245          } else {          } else {
3246            !!!cp (1021);            !!!cp (989);
3247            $value = $EntityChar->{$entity_name};            $self->{ca}->{value} .= '&' . $self->{s_kwd};
3248            $match = -1;            $self->{state} = $self->{prev_state};
3249              ## Reconsume.
3250              redo A;
3251            }
3252          }
3253        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3254          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3255            # 0..9
3256            !!!cp (1002);
3257            $self->{s_kwd} *= 0x10;
3258            $self->{s_kwd} += $self->{nc} - 0x0030;
3259            ## Stay in the state.
3260            !!!next-input-character;
3261            redo A;
3262          } elsif (0x0061 <= $self->{nc} and
3263                   $self->{nc} <= 0x0066) { # a..f
3264            !!!cp (1003);
3265            $self->{s_kwd} *= 0x10;
3266            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3267            ## Stay in the state.
3268            !!!next-input-character;
3269            redo A;
3270          } elsif (0x0041 <= $self->{nc} and
3271                   $self->{nc} <= 0x0046) { # A..F
3272            !!!cp (1004);
3273            $self->{s_kwd} *= 0x10;
3274            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3275            ## Stay in the state.
3276            !!!next-input-character;
3277            redo A;
3278          } elsif ($self->{nc} == 0x003B) { # ;
3279            !!!cp (1006);
3280            !!!next-input-character;
3281            #
3282          } else {
3283            !!!cp (1007);
3284            !!!parse-error (type => 'no refc',
3285                            line => $self->{line},
3286                            column => $self->{column});
3287            ## Reconsume.
3288            #
3289          }
3290    
3291          my $code = $self->{s_kwd};
3292          my $l = $self->{line_prev};
3293          my $c = $self->{column_prev};
3294          if ($charref_map->{$code}) {
3295            !!!cp (1008);
3296            !!!parse-error (type => 'invalid character reference',
3297                            text => (sprintf 'U+%04X', $code),
3298                            line => $l, column => $c);
3299            $code = $charref_map->{$code};
3300          } elsif ($code > 0x10FFFF) {
3301            !!!cp (1009);
3302            !!!parse-error (type => 'invalid character reference',
3303                            text => (sprintf 'U-%08X', $code),
3304                            line => $l, column => $c);
3305            $code = 0xFFFD;
3306          }
3307    
3308          if ($self->{prev_state} == DATA_STATE) {
3309            !!!cp (988);
3310            $self->{state} = $self->{prev_state};
3311            ## Reconsume.
3312            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3313                      line => $l, column => $c,
3314                     });
3315            redo A;
3316          } else {
3317            !!!cp (987);
3318            $self->{ca}->{value} .= chr $code;
3319            $self->{ca}->{has_reference} = 1;
3320            $self->{state} = $self->{prev_state};
3321            ## Reconsume.
3322            redo A;
3323          }
3324        } elsif ($self->{state} == ENTITY_NAME_STATE) {
3325          if (length $self->{s_kwd} < 30 and
3326              ## NOTE: Some number greater than the maximum length of entity name
3327              ((0x0041 <= $self->{nc} and # a
3328                $self->{nc} <= 0x005A) or # x
3329               (0x0061 <= $self->{nc} and # a
3330                $self->{nc} <= 0x007A) or # z
3331               (0x0030 <= $self->{nc} and # 0
3332                $self->{nc} <= 0x0039) or # 9
3333               $self->{nc} == 0x003B)) { # ;
3334            our $EntityChar;
3335            $self->{s_kwd} .= chr $self->{nc};
3336            if (defined $EntityChar->{$self->{s_kwd}}) {
3337              if ($self->{nc} == 0x003B) { # ;
3338                !!!cp (1020);
3339                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3340                $self->{entity__match} = 1;
3341                !!!next-input-character;
3342                #
3343              } else {
3344                !!!cp (1021);
3345                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3346                $self->{entity__match} = -1;
3347                ## Stay in the state.
3348                !!!next-input-character;
3349                redo A;
3350              }
3351            } else {
3352              !!!cp (1022);
3353              $self->{entity__value} .= chr $self->{nc};
3354              $self->{entity__match} *= 2;
3355              ## Stay in the state.
3356            !!!next-input-character;            !!!next-input-character;
3357              redo A;
3358            }
3359          }
3360    
3361          my $data;
3362          my $has_ref;
3363          if ($self->{entity__match} > 0) {
3364            !!!cp (1023);
3365            $data = $self->{entity__value};
3366            $has_ref = 1;
3367            #
3368          } elsif ($self->{entity__match} < 0) {
3369            !!!parse-error (type => 'no refc');
3370            if ($self->{prev_state} != DATA_STATE and # in attribute
3371                $self->{entity__match} < -1) {
3372              !!!cp (1024);
3373              $data = '&' . $self->{s_kwd};
3374              #
3375            } else {
3376              !!!cp (1025);
3377              $data = $self->{entity__value};
3378              $has_ref = 1;
3379              #
3380          }          }
3381        } else {        } else {
3382          !!!cp (1022);          !!!cp (1026);
3383          $value .= chr $self->{next_char};          !!!parse-error (type => 'bare ero',
3384          $match *= 2;                          line => $self->{line_prev},
3385          !!!next-input-character;                          column => $self->{column_prev} - length $self->{s_kwd});
3386            $data = '&' . $self->{s_kwd};
3387            #
3388        }        }
3389      }    
3390              ## NOTE: In these cases, when a character reference is found,
3391      if ($match > 0) {        ## it is consumed and a character token is returned, or, otherwise,
3392        !!!cp (1023);        ## nothing is consumed and returned, according to the spec algorithm.
3393        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,        ## In this implementation, anything that has been examined by the
3394                line => $l, column => $c,        ## tokenizer is appended to the parent element or the attribute value
3395               };        ## as string, either literal string when no character reference or
3396      } elsif ($match < 0) {        ## entity-replaced string otherwise, in this stage, since any characters
3397        !!!parse-error (type => 'no refc', line => $l, column => $c);        ## that would not be consumed are appended in the data state or in an
3398        if ($in_attr and $match < -1) {        ## appropriate attribute value state anyway.
3399          !!!cp (1024);  
3400          return {type => CHARACTER_TOKEN, data => '&'.$entity_name,        if ($self->{prev_state} == DATA_STATE) {
3401                  line => $l, column => $c,          !!!cp (986);
3402                 };          $self->{state} = $self->{prev_state};
3403        } else {          ## Reconsume.
3404          !!!cp (1025);          !!!emit ({type => CHARACTER_TOKEN,
3405          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,                    data => $data,
3406                  line => $l, column => $c,                    line => $self->{line_prev},
3407                 };                    column => $self->{column_prev} + 1 - length $self->{s_kwd},
3408                     });
3409            redo A;
3410          } else {
3411            !!!cp (985);
3412            $self->{ca}->{value} .= $data;
3413            $self->{ca}->{has_reference} = 1 if $has_ref;
3414            $self->{state} = $self->{prev_state};
3415            ## Reconsume.
3416            redo A;
3417        }        }
3418      } else {      } else {
3419        !!!cp (1026);        die "$0: $self->{state}: Unknown state";
       !!!parse-error (type => 'bare ero', line => $l, column => $c);  
       ## NOTE: "No characters are consumed" in the spec.  
       return {type => CHARACTER_TOKEN, data => '&'.$value,  
               line => $l, column => $c,  
              };  
3420      }      }
3421    } else {    } # A  
3422      !!!cp (1027);  
3423      ## no characters are consumed    die "$0: _get_next_token: unexpected case";
3424      !!!parse-error (type => 'bare ero', line => $l, column => $c);  } # _get_next_token
     return undef;  
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3425    
3426  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3427    my $self = shift;    my $self = shift;
# Line 3017  sub _initialize_tree_constructor ($) { Line 3430  sub _initialize_tree_constructor ($) {
3430    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3431    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3432    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3433      $self->{document}->set_user_data (manakai_source_line => 1);
3434      $self->{document}->set_user_data (manakai_source_column => 1);
3435  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3436    
3437  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 3071  sub _tree_construction_initial ($) { Line 3486  sub _tree_construction_initial ($) {
3486        ## language.        ## language.
3487        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
3488        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3489        $doctype_name =~ tr/a-z/A-Z/;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3490        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3491            defined $token->{public_identifier} or            defined $token->{sysid}) {
           defined $token->{system_identifier}) {  
3492          !!!cp ('t1');          !!!cp ('t1');
3493          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
3494        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3495          !!!cp ('t2');          !!!cp ('t2');
         ## ISSUE: ASCII case-insensitive? (in fact it does not matter)  
3496          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
3497          } elsif (defined $token->{pubid}) {
3498            if ($token->{pubid} eq 'XSLT-compat') {
3499              !!!cp ('t1.2');
3500              !!!parse-error (type => 'XSLT-compat', token => $token,
3501                              level => $self->{level}->{should});
3502            } else {
3503              !!!parse-error (type => 'not HTML5', token => $token);
3504            }
3505        } else {        } else {
3506          !!!cp ('t3');          !!!cp ('t3');
3507            #
3508        }        }
3509                
3510        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
3511          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3512        ## NOTE: Default value for both |public_id| and |system_id| attributes        ## NOTE: Default value for both |public_id| and |system_id| attributes
3513        ## 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.
3514        $doctype->public_id ($token->{public_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3515            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};  
3516        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3517        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3518        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
# Line 3100  sub _tree_construction_initial ($) { Line 3520  sub _tree_construction_initial ($) {
3520        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3521          !!!cp ('t4');          !!!cp ('t4');
3522          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3523        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3524          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3525          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3526          if ({          my $prefix = [
3527            "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3528            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3529            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3530            "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 1//",
3531            "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 2//",
3532            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3533            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3534            "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT//",
3535            "-//IETF//DTD HTML 2.0//EN" => 1,            "-//IETF//DTD HTML 2.0//",
3536            "-//IETF//DTD HTML 2.1E//EN" => 1,            "-//IETF//DTD HTML 2.1E//",
3537            "-//IETF//DTD HTML 3.0//EN" => 1,            "-//IETF//DTD HTML 3.0//",
3538            "-//IETF//DTD HTML 3.0//EN//" => 1,            "-//IETF//DTD HTML 3.2 FINAL//",
3539            "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,            "-//IETF//DTD HTML 3.2//",
3540            "-//IETF//DTD HTML 3.2//EN" => 1,            "-//IETF//DTD HTML 3//",
3541            "-//IETF//DTD HTML 3//EN" => 1,            "-//IETF//DTD HTML LEVEL 0//",
3542            "-//IETF//DTD HTML LEVEL 0//EN" => 1,            "-//IETF//DTD HTML LEVEL 1//",
3543            "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,            "-//IETF//DTD HTML LEVEL 2//",
3544            "-//IETF//DTD HTML LEVEL 1//EN" => 1,            "-//IETF//DTD HTML LEVEL 3//",
3545            "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 0//",
3546            "-//IETF//DTD HTML LEVEL 2//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 1//",
3547            "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 2//",
3548            "-//IETF//DTD HTML LEVEL 3//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 3//",
3549            "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,            "-//IETF//DTD HTML STRICT//",
3550            "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,            "-//IETF//DTD HTML//",
3551            "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,            "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3552            "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3553            "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3554            "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3555            "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3556            "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3557            "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3558            "-//IETF//DTD HTML STRICT//EN" => 1,            "-//NETSCAPE COMM. CORP.//DTD HTML//",
3559            "-//IETF//DTD HTML STRICT//EN//2.0" => 1,            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3560            "-//IETF//DTD HTML STRICT//EN//3.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3561            "-//IETF//DTD HTML//EN" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3562            "-//IETF//DTD HTML//EN//2.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3563            "-//IETF//DTD HTML//EN//3.0" => 1,            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3564            "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3565            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3566            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3567            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3568            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3569            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,            "-//W3C//DTD HTML 3 1995-03-24//",
3570            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,            "-//W3C//DTD HTML 3.2 DRAFT//",
3571            "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,            "-//W3C//DTD HTML 3.2 FINAL//",
3572            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,            "-//W3C//DTD HTML 3.2//",
3573            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,            "-//W3C//DTD HTML 3.2S DRAFT//",
3574            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 FRAMESET//",
3575            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3576            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3577            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3578            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,            "-//W3C//DTD W3 HTML//",
3579            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,            "-//W3O//DTD W3 HTML 3.0//",
3580            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3581            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML//",
3582            "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,          ]; # $prefix
3583            "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,          my $match;
3584            "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,          for (@$prefix) {
3585            "-//W3C//DTD HTML 3.2//EN" => 1,            if (substr ($prefix, 0, length $_) eq $_) {
3586            "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,              $match = 1;
3587            "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,              last;
3588            "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,            }
3589            "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,          }
3590            "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,          if ($match or
3591            "-//W3C//DTD W3 HTML//EN" => 1,              $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3592            "-//W3O//DTD W3 HTML 3.0//EN" => 1,              $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3593            "-//W3O//DTD W3 HTML 3.0//EN//" => 1,              $pubid eq "HTML") {
           "-//W3O//DTD W3 HTML STRICT 3.0//EN//" => 1,  
           "-//WEBTECHS//DTD MOZILLA HTML 2.0//EN" => 1,  
           "-//WEBTECHS//DTD MOZILLA HTML//EN" => 1,  
           "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" => 1,  
           "HTML" => 1,  
         }->{$pubid}) {  
3594            !!!cp ('t5');            !!!cp ('t5');
3595            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3596          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3597                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3598            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3599              !!!cp ('t6');              !!!cp ('t6');
3600              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3601            } else {            } else {
3602              !!!cp ('t7');              !!!cp ('t7');
3603              $self->{document}->manakai_compat_mode ('limited quirks');              $self->{document}->manakai_compat_mode ('limited quirks');
3604            }            }
3605          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3606                   $pubid eq "-//W3C//DTD XHTML 1.0 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3607            !!!cp ('t8');            !!!cp ('t8');
3608            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3609          } else {          } else {
# Line 3198  sub _tree_construction_initial ($) { Line 3612  sub _tree_construction_initial ($) {
3612        } else {        } else {
3613          !!!cp ('t10');          !!!cp ('t10');
3614        }        }
3615        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3616          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3617          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3618          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
3619            ## TODO: Check the spec: PUBLIC "(limited quirks)" "(quirks)"            ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3620              ## marked as quirks.
3621            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3622            !!!cp ('t11');            !!!cp ('t11');
3623          } else {          } else {
# Line 3228  sub _tree_construction_initial ($) { Line 3643  sub _tree_construction_initial ($) {
3643        !!!ack-later;        !!!ack-later;
3644        return;        return;
3645      } elsif ($token->{type} == CHARACTER_TOKEN) {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3646        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3647          ## Ignore the token          ## Ignore the token
3648    
3649          unless (length $token->{data}) {          unless (length $token->{data}) {
# Line 3285  sub _tree_construction_root_element ($) Line 3700  sub _tree_construction_root_element ($)
3700          !!!next-token;          !!!next-token;
3701          redo B;          redo B;
3702        } elsif ($token->{type} == CHARACTER_TOKEN) {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3703          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3704            ## Ignore the token.            ## Ignore the token.
3705    
3706            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 3374  sub _reset_insertion_mode ($) { Line 3789  sub _reset_insertion_mode ($) {
3789        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3790          $last = 1;          $last = 1;
3791          if (defined $self->{inner_html_node}) {          if (defined $self->{inner_html_node}) {
3792            if ($self->{inner_html_node}->[1] & TABLE_CELL_EL) {            !!!cp ('t28');
3793              !!!cp ('t27');            $node = $self->{inner_html_node};
3794              #          } else {
3795            } else {            die "_reset_insertion_mode: t27";
             !!!cp ('t28');  
             $node = $self->{inner_html_node};  
           }  
3796          }          }
3797        }        }
3798              
3799      ## Step 4..14        ## Step 4..14
3800      my $new_mode;        my $new_mode;
3801      if ($node->[1] & FOREIGN_EL) {        if ($node->[1] & FOREIGN_EL) {
3802        ## NOTE: Strictly spaking, the line below only applies to MathML and          !!!cp ('t28.1');
3803        ## SVG elements.  Currently the HTML syntax supports only MathML and          ## NOTE: Strictly spaking, the line below only applies to MathML and
3804        ## SVG elements as foreigners.          ## SVG elements.  Currently the HTML syntax supports only MathML and
3805        $new_mode = $self->{insertion_mode} | IN_FOREIGN_CONTENT_IM;          ## SVG elements as foreigners.
3806        ## ISSUE: What is set as the secondary insertion mode?          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3807      } else {        } elsif ($node->[1] & TABLE_CELL_EL) {
3808        $new_mode = {          if ($last) {
3809              !!!cp ('t28.2');
3810              #
3811            } else {
3812              !!!cp ('t28.3');
3813              $new_mode = IN_CELL_IM;
3814            }
3815          } else {
3816            !!!cp ('t28.4');
3817            $new_mode = {
3818                        select => IN_SELECT_IM,                        select => IN_SELECT_IM,
3819                        ## NOTE: |option| and |optgroup| do not set                        ## NOTE: |option| and |optgroup| do not set
3820                        ## insertion mode to "in select" by themselves.                        ## insertion mode to "in select" by themselves.
                       td => IN_CELL_IM,  
                       th => IN_CELL_IM,  
3821                        tr => IN_ROW_IM,                        tr => IN_ROW_IM,
3822                        tbody => IN_TABLE_BODY_IM,                        tbody => IN_TABLE_BODY_IM,
3823                        thead => IN_TABLE_BODY_IM,                        thead => IN_TABLE_BODY_IM,
# Line 3410  sub _reset_insertion_mode ($) { Line 3829  sub _reset_insertion_mode ($) {
3829                        body => IN_BODY_IM,                        body => IN_BODY_IM,
3830                        frameset => IN_FRAMESET_IM,                        frameset => IN_FRAMESET_IM,
3831                       }->{$node->[0]->manakai_local_name};                       }->{$node->[0]->manakai_local_name};
3832      }        }
3833      $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3834                
3835        ## Step 15        ## Step 15
3836        if ($node->[1] & HTML_EL) {        if ($node->[1] & HTML_EL) {
# Line 3585  sub _tree_construction_main ($) { Line 4004  sub _tree_construction_main ($) {
4004        ## NOTE: An end-of-file token.        ## NOTE: An end-of-file token.
4005        if ($content_model_flag == CDATA_CONTENT_MODEL) {        if ($content_model_flag == CDATA_CONTENT_MODEL) {
4006          !!!cp ('t43');          !!!cp ('t43');
4007          !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token);          !!!parse-error (type => 'in CDATA:#eof', token => $token);
4008        } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {        } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
4009          !!!cp ('t44');          !!!cp ('t44');
4010          !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token);          !!!parse-error (type => 'in RCDATA:#eof', token => $token);
4011        } else {        } else {
4012          die "$0: $content_model_flag in parse_rcdata";          die "$0: $content_model_flag in parse_rcdata";
4013        }        }
# Line 3625  sub _tree_construction_main ($) { Line 4044  sub _tree_construction_main ($) {
4044        ## Ignore the token        ## Ignore the token
4045      } else {      } else {
4046        !!!cp ('t48');        !!!cp ('t48');
4047        !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token);        !!!parse-error (type => 'in CDATA:#eof', token => $token);
4048        ## ISSUE: And ignore?        ## ISSUE: And ignore?
4049        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4050      }      }
# Line 3676  sub _tree_construction_main ($) { Line 4095  sub _tree_construction_main ($) {
4095        } # AFE        } # AFE
4096        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4097          !!!cp ('t53');          !!!cp ('t53');
4098          !!!parse-error (type => 'unmatched end tag:'.$tag_name, token => $end_tag_token);          !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4099          ## Ignore the token          ## Ignore the token
4100          !!!next-token;          !!!next-token;
4101          return;          return;
# Line 3693  sub _tree_construction_main ($) { Line 4112  sub _tree_construction_main ($) {
4112              last INSCOPE;              last INSCOPE;
4113            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4114              !!!cp ('t55');              !!!cp ('t55');
4115              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},              !!!parse-error (type => 'unmatched end tag',
4116                                text => $token->{tag_name},
4117                              token => $end_tag_token);                              token => $end_tag_token);
4118              ## Ignore the token              ## Ignore the token
4119              !!!next-token;              !!!next-token;
# Line 3706  sub _tree_construction_main ($) { Line 4126  sub _tree_construction_main ($) {
4126        } # INSCOPE        } # INSCOPE
4127        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4128          !!!cp ('t57');          !!!cp ('t57');
4129          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},          !!!parse-error (type => 'unmatched end tag',
4130                            text => $token->{tag_name},
4131                          token => $end_tag_token);                          token => $end_tag_token);
4132          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4133          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
# Line 3715  sub _tree_construction_main ($) { Line 4136  sub _tree_construction_main ($) {
4136        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4137          !!!cp ('t58');          !!!cp ('t58');
4138          !!!parse-error (type => 'not closed',          !!!parse-error (type => 'not closed',
4139                          value => $self->{open_elements}->[-1]->[0]                          text => $self->{open_elements}->[-1]->[0]
4140                              ->manakai_local_name,                              ->manakai_local_name,
4141                          token => $end_tag_token);                          token => $end_tag_token);
4142        }        }
# Line 3924  sub _tree_construction_main ($) { Line 4345  sub _tree_construction_main ($) {
4345    B: while (1) {    B: while (1) {
4346      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
4347        !!!cp ('t73');        !!!cp ('t73');
4348        !!!parse-error (type => 'DOCTYPE in the middle', token => $token);        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4349        ## Ignore the token        ## Ignore the token
4350        ## Stay in the phase        ## Stay in the phase
4351        !!!next-token;        !!!next-token;
# Line 3933  sub _tree_construction_main ($) { Line 4354  sub _tree_construction_main ($) {
4354               $token->{tag_name} eq 'html') {               $token->{tag_name} eq 'html') {
4355        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4356          !!!cp ('t79');          !!!cp ('t79');
4357          !!!parse-error (type => 'after html:html', token => $token);          !!!parse-error (type => 'after html', text => 'html', token => $token);
4358          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
4359        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4360          !!!cp ('t80');          !!!cp ('t80');
4361          !!!parse-error (type => 'after html:html', token => $token);          !!!parse-error (type => 'after html', text => 'html', token => $token);
4362          $self->{insertion_mode} = AFTER_FRAMESET_IM;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4363        } else {        } else {
4364          !!!cp ('t81');          !!!cp ('t81');
# Line 3988  sub _tree_construction_main ($) { Line 4409  sub _tree_construction_main ($) {
4409            #            #
4410          } elsif ({          } elsif ({
4411                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4412                    center => 1, code => 1, dd => 1, div => 1, dl => 1, em => 1,                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4413                    embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1, ## No h4!                    em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4414                    h5 => 1, h6 => 1, head => 1, hr => 1, i => 1, img => 1,                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4415                    li => 1, menu => 1, meta => 1, nobr => 1, p => 1, pre => 1,                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4416                    ruby => 1, s => 1, small => 1, span => 1, strong => 1,                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4417                    sub => 1, sup => 1, table => 1, tt => 1, u => 1, ul => 1,                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4418                    var => 1,                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4419                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
4420            !!!cp ('t87.2');            !!!cp ('t87.2');
4421            !!!parse-error (type => 'not closed',            !!!parse-error (type => 'not closed',
4422                            value => $self->{open_elements}->[-1]->[0]                            text => $self->{open_elements}->[-1]->[0]
4423                                ->manakai_local_name,                                ->manakai_local_name,
4424                            token => $token);                            token => $token);
4425    
# Line 4074  sub _tree_construction_main ($) { Line 4495  sub _tree_construction_main ($) {
4495          !!!cp ('t87.5');          !!!cp ('t87.5');
4496          #          #
4497        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
         ## NOTE: "using the rules for secondary insertion mode" then "continue"  
4498          !!!cp ('t87.6');          !!!cp ('t87.6');
4499          #          !!!parse-error (type => 'not closed',
4500          ## TODO: ...                          text => $self->{open_elements}->[-1]->[0]
4501                                ->manakai_local_name,
4502                            token => $token);
4503    
4504            pop @{$self->{open_elements}}
4505                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4506    
4507            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4508            ## Reprocess.
4509            next B;
4510        } else {        } else {
4511          die "$0: $token->{type}: Unknown token type";                  die "$0: $token->{type}: Unknown token type";        
4512        }        }
# Line 4085  sub _tree_construction_main ($) { Line 4514  sub _tree_construction_main ($) {
4514    
4515      if ($self->{insertion_mode} & HEAD_IMS) {      if ($self->{insertion_mode} & HEAD_IMS) {
4516        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4517          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4518            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4519              !!!cp ('t88.2');              !!!cp ('t88.2');
4520              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4521                #
4522            } else {            } else {
4523              !!!cp ('t88.1');              !!!cp ('t88.1');
4524              ## Ignore the token.              ## Ignore the token.
4525              !!!next-token;              #
             next B;  
4526            }            }
4527            unless (length $token->{data}) {            unless (length $token->{data}) {
4528              !!!cp ('t88');              !!!cp ('t88');
4529              !!!next-token;              !!!next-token;
4530              next B;              next B;
4531            }            }
4532    ## TODO: set $token->{column} appropriately
4533          }          }
4534    
4535          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
# Line 4118  sub _tree_construction_main ($) { Line 4548  sub _tree_construction_main ($) {
4548            !!!cp ('t90');            !!!cp ('t90');
4549            ## As if </noscript>            ## As if </noscript>
4550            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4551            !!!parse-error (type => 'in noscript:#character', token => $token);            !!!parse-error (type => 'in noscript:#text', token => $token);
4552                        
4553            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4554            ## As if </head>            ## As if </head>
# Line 4155  sub _tree_construction_main ($) { Line 4585  sub _tree_construction_main ($) {
4585              next B;              next B;
4586            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4587              !!!cp ('t93.2');              !!!cp ('t93.2');
4588              !!!parse-error (type => 'after head:head', token => $token); ## TODO: error type              !!!parse-error (type => 'after head', text => 'head',
4589                                token => $token);
4590              ## Ignore the token              ## Ignore the token
4591              !!!nack ('t93.3');              !!!nack ('t93.3');
4592              !!!next-token;              !!!next-token;
4593              next B;              next B;
4594            } else {            } else {
4595              !!!cp ('t95');              !!!cp ('t95');
4596              !!!parse-error (type => 'in head:head', token => $token); # or in head noscript              !!!parse-error (type => 'in head:head',
4597                                token => $token); # or in head noscript
4598              ## Ignore the token              ## Ignore the token
4599              !!!nack ('t95.1');              !!!nack ('t95.1');
4600              !!!next-token;              !!!next-token;
# Line 4187  sub _tree_construction_main ($) { Line 4619  sub _tree_construction_main ($) {
4619                  !!!cp ('t98');                  !!!cp ('t98');
4620                  ## As if </noscript>                  ## As if </noscript>
4621                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4622                  !!!parse-error (type => 'in noscript:base', token => $token);                  !!!parse-error (type => 'in noscript', text => 'base',
4623                                    token => $token);
4624                                
4625                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4626                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
# Line 4198  sub _tree_construction_main ($) { Line 4631  sub _tree_construction_main ($) {
4631                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4632                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4633                  !!!cp ('t100');                  !!!cp ('t100');
4634                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4635                                    text => $token->{tag_name}, token => $token);
4636                  push @{$self->{open_elements}},                  push @{$self->{open_elements}},
4637                      [$self->{head_element}, $el_category->{head}];                      [$self->{head_element}, $el_category->{head}];
4638                } else {                } else {
# Line 4215  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 ('t102');                  !!!cp ('t102');
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 {
# Line 4232  sub _tree_construction_main ($) { Line 4667  sub _tree_construction_main ($) {
4667                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4668                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4669                  !!!cp ('t104');                  !!!cp ('t104');
4670                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4671                                    text => $token->{tag_name}, token => $token);
4672                  push @{$self->{open_elements}},                  push @{$self->{open_elements}},
4673                      [$self->{head_element}, $el_category->{head}];                      [$self->{head_element}, $el_category->{head}];
4674                } else {                } else {
# Line 4256  sub _tree_construction_main ($) { Line 4692  sub _tree_construction_main ($) {
4692                                                 ->{has_reference});                                                 ->{has_reference});
4693                  } elsif ($token->{attributes}->{content}) {                  } elsif ($token->{attributes}->{content}) {
4694                    if ($token->{attributes}->{content}->{value}                    if ($token->{attributes}->{content}->{value}
4695                        =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4696                            [\x09-\x0D\x20]*=                            [\x09\x0A\x0C\x0D\x20]*=
4697                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4698                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                            ([^"'\x09\x0A\x0C\x0D\x20]
4699                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4700                      !!!cp ('t107');                      !!!cp ('t107');
4701                      ## NOTE: Whether the encoding is supported or not is handled                      ## NOTE: Whether the encoding is supported or not is handled
4702                      ## in the {change_encoding} callback.                      ## in the {change_encoding} callback.
# Line 4301  sub _tree_construction_main ($) { Line 4738  sub _tree_construction_main ($) {
4738                  !!!cp ('t111');                  !!!cp ('t111');
4739                  ## As if </noscript>                  ## As if </noscript>
4740                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4741                  !!!parse-error (type => 'in noscript:title', token => $token);                  !!!parse-error (type => 'in noscript', text => 'title',
4742                                    token => $token);
4743                                
4744                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4745                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4746                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4747                  !!!cp ('t112');                  !!!cp ('t112');
4748                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4749                                    text => $token->{tag_name}, token => $token);
4750                  push @{$self->{open_elements}},                  push @{$self->{open_elements}},
4751                      [$self->{head_element}, $el_category->{head}];                      [$self->{head_element}, $el_category->{head}];
4752                } else {                } else {
# Line 4321  sub _tree_construction_main ($) { Line 4760  sub _tree_construction_main ($) {
4760                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4761                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4762                next B;                next B;
4763              } elsif ($token->{tag_name} eq 'style') {              } elsif ($token->{tag_name} eq 'style' or
4764                         $token->{tag_name} eq 'noframes') {
4765                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4766                ## insertion mode IN_HEAD_IM)                ## insertion mode IN_HEAD_IM)
4767                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4768                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4769                  !!!cp ('t114');                  !!!cp ('t114');
4770                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4771                                    text => $token->{tag_name}, token => $token);
4772                  push @{$self->{open_elements}},                  push @{$self->{open_elements}},
4773                      [$self->{head_element}, $el_category->{head}];                      [$self->{head_element}, $el_category->{head}];
4774                } else {                } else {
# Line 4348  sub _tree_construction_main ($) { Line 4789  sub _tree_construction_main ($) {
4789                  next B;                  next B;
4790                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4791                  !!!cp ('t117');                  !!!cp ('t117');
4792                  !!!parse-error (type => 'in noscript:noscript', token => $token);                  !!!parse-error (type => 'in noscript', text => 'noscript',
4793                                    token => $token);
4794                  ## Ignore the token                  ## Ignore the token
4795                  !!!nack ('t117.1');                  !!!nack ('t117.1');
4796                  !!!next-token;                  !!!next-token;
# Line 4362  sub _tree_construction_main ($) { Line 4804  sub _tree_construction_main ($) {
4804                  !!!cp ('t119');                  !!!cp ('t119');
4805                  ## As if </noscript>                  ## As if </noscript>
4806                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4807                  !!!parse-error (type => 'in noscript:script', token => $token);                  !!!parse-error (type => 'in noscript', text => 'script',
4808                                    token => $token);
4809                                
4810                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4811                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4812                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4813                  !!!cp ('t120');                  !!!cp ('t120');
4814                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4815                                    text => $token->{tag_name}, token => $token);
4816                  push @{$self->{open_elements}},                  push @{$self->{open_elements}},
4817                      [$self->{head_element}, $el_category->{head}];                      [$self->{head_element}, $el_category->{head}];
4818                } else {                } else {
# Line 4386  sub _tree_construction_main ($) { Line 4830  sub _tree_construction_main ($) {
4830                  !!!cp ('t122');                  !!!cp ('t122');
4831                  ## As if </noscript>                  ## As if </noscript>
4832                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4833                  !!!parse-error (type => 'in noscript:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'in noscript',
4834                                    text => $token->{tag_name}, token => $token);
4835                                    
4836                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4837                  ## As if </head>                  ## As if </head>
# Line 4425  sub _tree_construction_main ($) { Line 4870  sub _tree_construction_main ($) {
4870                !!!cp ('t129');                !!!cp ('t129');
4871                ## As if </noscript>                ## As if </noscript>
4872                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4873                !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'in noscript:/',
4874                                  text => $token->{tag_name}, token => $token);
4875                                
4876                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
4877                ## As if </head>                ## As if </head>
# Line 4468  sub _tree_construction_main ($) { Line 4914  sub _tree_construction_main ($) {
4914                  !!!cp ('t133');                  !!!cp ('t133');
4915                  ## As if </noscript>                  ## As if </noscript>
4916                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4917                  !!!parse-error (type => 'in noscript:/head', token => $token);                  !!!parse-error (type => 'in noscript:/',
4918                                    text => 'head', token => $token);
4919                                    
4920                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4921                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
# Line 4483  sub _tree_construction_main ($) { Line 4930  sub _tree_construction_main ($) {
4930                  next B;                  next B;
4931                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4932                  !!!cp ('t134.1');                  !!!cp ('t134.1');
4933                  !!!parse-error (type => 'unmatched end tag:head', token => $token);                  !!!parse-error (type => 'unmatched end tag', text => 'head',
4934                                    token => $token);
4935                  ## Ignore the token                  ## Ignore the token
4936                  !!!next-token;                  !!!next-token;
4937                  next B;                  next B;
# Line 4500  sub _tree_construction_main ($) { Line 4948  sub _tree_construction_main ($) {
4948                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
4949                         $self->{insertion_mode} == AFTER_HEAD_IM) {                         $self->{insertion_mode} == AFTER_HEAD_IM) {
4950                  !!!cp ('t137');                  !!!cp ('t137');
4951                  !!!parse-error (type => 'unmatched end tag:noscript', token => $token);                  !!!parse-error (type => 'unmatched end tag',
4952                                    text => 'noscript', token => $token);
4953                  ## Ignore the token ## ISSUE: An issue in the spec.                  ## Ignore the token ## ISSUE: An issue in the spec.
4954                  !!!next-token;                  !!!next-token;
4955                  next B;                  next B;
# Line 4515  sub _tree_construction_main ($) { Line 4964  sub _tree_construction_main ($) {
4964                    $self->{insertion_mode} == IN_HEAD_IM or                    $self->{insertion_mode} == IN_HEAD_IM or
4965                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4966                  !!!cp ('t140');                  !!!cp ('t140');
4967                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
4968                                    text => $token->{tag_name}, token => $token);
4969                  ## Ignore the token                  ## Ignore the token
4970                  !!!next-token;                  !!!next-token;
4971                  next B;                  next B;
4972                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4973                  !!!cp ('t140.1');                  !!!cp ('t140.1');
4974                  !!!parse-error (type => 'unmatched end tag:' . $token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
4975                                    text => $token->{tag_name}, token => $token);
4976                  ## Ignore the token                  ## Ignore the token
4977                  !!!next-token;                  !!!next-token;
4978                  next B;                  next B;
# Line 4530  sub _tree_construction_main ($) { Line 4981  sub _tree_construction_main ($) {
4981                }                }
4982              } elsif ($token->{tag_name} eq 'p') {              } elsif ($token->{tag_name} eq 'p') {
4983                !!!cp ('t142');                !!!cp ('t142');
4984                !!!parse-error (type => 'unmatched end tag:p', token => $token);                !!!parse-error (type => 'unmatched end tag',
4985                                  text => $token->{tag_name}, token => $token);
4986                ## Ignore the token                ## Ignore the token
4987                !!!next-token;                !!!next-token;
4988                next B;                next B;
# Line 4553  sub _tree_construction_main ($) { Line 5005  sub _tree_construction_main ($) {
5005                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5006                  !!!cp ('t143.3');                  !!!cp ('t143.3');
5007                  ## ISSUE: Two parse errors for <head><noscript></br>                  ## ISSUE: Two parse errors for <head><noscript></br>
5008                  !!!parse-error (type => 'unmatched end tag:br', token => $token);                  !!!parse-error (type => 'unmatched end tag',
5009                                    text => 'br', token => $token);
5010                  ## As if </noscript>                  ## As if </noscript>
5011                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5012                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
# Line 4572  sub _tree_construction_main ($) { Line 5025  sub _tree_construction_main ($) {
5025                }                }
5026    
5027                ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.                ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5028                !!!parse-error (type => 'unmatched end tag:br', token => $token);                !!!parse-error (type => 'unmatched end tag',
5029                                  text => 'br', token => $token);
5030                ## Ignore the token                ## Ignore the token
5031                !!!next-token;                !!!next-token;
5032                next B;                next B;
5033              } else {              } else {
5034                !!!cp ('t145');                !!!cp ('t145');
5035                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
5036                                  text => $token->{tag_name}, token => $token);
5037                ## Ignore the token                ## Ignore the token
5038                !!!next-token;                !!!next-token;
5039                next B;                next B;
# Line 4588  sub _tree_construction_main ($) { Line 5043  sub _tree_construction_main ($) {
5043                !!!cp ('t146');                !!!cp ('t146');
5044                ## As if </noscript>                ## As if </noscript>
5045                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5046                !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'in noscript:/',
5047                                  text => $token->{tag_name}, token => $token);
5048                                
5049                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
5050                ## As if </head>                ## As if </head>
# Line 4604  sub _tree_construction_main ($) { Line 5060  sub _tree_construction_main ($) {
5060              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5061  ## ISSUE: This case cannot be reached?  ## ISSUE: This case cannot be reached?
5062                !!!cp ('t148');                !!!cp ('t148');
5063                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
5064                                  text => $token->{tag_name}, token => $token);
5065                ## Ignore the token ## ISSUE: An issue in the spec.                ## Ignore the token ## ISSUE: An issue in the spec.
5066                !!!next-token;                !!!next-token;
5067                next B;                next B;
# Line 4715  sub _tree_construction_main ($) { Line 5172  sub _tree_construction_main ($) {
5172    
5173                  !!!cp ('t153');                  !!!cp ('t153');
5174                  !!!parse-error (type => 'start tag not allowed',                  !!!parse-error (type => 'start tag not allowed',
5175                      value => $token->{tag_name}, token => $token);                      text => $token->{tag_name}, token => $token);
5176                  ## Ignore the token                  ## Ignore the token
5177                  !!!nack ('t153.1');                  !!!nack ('t153.1');
5178                  !!!next-token;                  !!!next-token;
5179                  next B;                  next B;
5180                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5181                  !!!parse-error (type => 'not closed:caption', token => $token);                  !!!parse-error (type => 'not closed', text => 'caption',
5182                                    token => $token);
5183                                    
5184                  ## NOTE: As if </caption>.                  ## NOTE: As if </caption>.
5185                  ## have a table element in table scope                  ## have a table element in table scope
# Line 4741  sub _tree_construction_main ($) { Line 5199  sub _tree_construction_main ($) {
5199    
5200                    !!!cp ('t157');                    !!!cp ('t157');
5201                    !!!parse-error (type => 'start tag not allowed',                    !!!parse-error (type => 'start tag not allowed',
5202                                    value => $token->{tag_name}, token => $token);                                    text => $token->{tag_name}, token => $token);
5203                    ## Ignore the token                    ## Ignore the token
5204                    !!!nack ('t157.1');                    !!!nack ('t157.1');
5205                    !!!next-token;                    !!!next-token;
# Line 4758  sub _tree_construction_main ($) { Line 5216  sub _tree_construction_main ($) {
5216                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5217                    !!!cp ('t159');                    !!!cp ('t159');
5218                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
5219                                    value => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
5220                                        ->manakai_local_name,                                        ->manakai_local_name,
5221                                    token => $token);                                    token => $token);
5222                  } else {                  } else {
# Line 4800  sub _tree_construction_main ($) { Line 5258  sub _tree_construction_main ($) {
5258                  } # INSCOPE                  } # INSCOPE
5259                    unless (defined $i) {                    unless (defined $i) {
5260                      !!!cp ('t165');                      !!!cp ('t165');
5261                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                      !!!parse-error (type => 'unmatched end tag',
5262                                        text => $token->{tag_name},
5263                                        token => $token);
5264                      ## Ignore the token                      ## Ignore the token
5265                      !!!next-token;                      !!!next-token;
5266                      next B;                      next B;
# Line 4817  sub _tree_construction_main ($) { Line 5277  sub _tree_construction_main ($) {
5277                          ne $token->{tag_name}) {                          ne $token->{tag_name}) {
5278                    !!!cp ('t167');                    !!!cp ('t167');
5279                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
5280                                    value => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
5281                                        ->manakai_local_name,                                        ->manakai_local_name,
5282                                    token => $token);                                    token => $token);
5283                  } else {                  } else {
# Line 4834  sub _tree_construction_main ($) { Line 5294  sub _tree_construction_main ($) {
5294                  next B;                  next B;
5295                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5296                  !!!cp ('t169');                  !!!cp ('t169');
5297                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5298                                    text => $token->{tag_name}, token => $token);
5299                  ## Ignore the token                  ## Ignore the token
5300                  !!!next-token;                  !!!next-token;
5301                  next B;                  next B;
# Line 4861  sub _tree_construction_main ($) { Line 5322  sub _tree_construction_main ($) {
5322    
5323                    !!!cp ('t173');                    !!!cp ('t173');
5324                    !!!parse-error (type => 'unmatched end tag',                    !!!parse-error (type => 'unmatched end tag',
5325                                    value => $token->{tag_name}, token => $token);                                    text => $token->{tag_name}, token => $token);
5326                    ## Ignore the token                    ## Ignore the token
5327                    !!!next-token;                    !!!next-token;
5328                    next B;                    next B;
# Line 4877  sub _tree_construction_main ($) { Line 5338  sub _tree_construction_main ($) {
5338                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5339                    !!!cp ('t175');                    !!!cp ('t175');
5340                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
5341                                    value => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
5342                                        ->manakai_local_name,                                        ->manakai_local_name,
5343                                    token => $token);                                    token => $token);
5344                  } else {                  } else {
# Line 4894  sub _tree_construction_main ($) { Line 5355  sub _tree_construction_main ($) {
5355                  next B;                  next B;
5356                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5357                  !!!cp ('t177');                  !!!cp ('t177');
5358                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5359                                    text => $token->{tag_name}, token => $token);
5360                  ## Ignore the token                  ## Ignore the token
5361                  !!!next-token;                  !!!next-token;
5362                  next B;                  next B;
# Line 4937  sub _tree_construction_main ($) { Line 5399  sub _tree_construction_main ($) {
5399    
5400                  !!!cp ('t182');                  !!!cp ('t182');
5401                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
5402                      value => $token->{tag_name}, token => $token);                      text => $token->{tag_name}, token => $token);
5403                  ## Ignore the token                  ## Ignore the token
5404                  !!!next-token;                  !!!next-token;
5405                  next B;                  next B;
5406                } # INSCOPE                } # INSCOPE
5407              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
5408                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5409                !!!parse-error (type => 'not closed:caption', token => $token);                !!!parse-error (type => 'not closed', text => 'caption',
5410                                  token => $token);
5411    
5412                ## As if </caption>                ## As if </caption>
5413                ## have a table element in table scope                ## have a table element in table scope
# Line 4962  sub _tree_construction_main ($) { Line 5425  sub _tree_construction_main ($) {
5425                } # INSCOPE                } # INSCOPE
5426                unless (defined $i) {                unless (defined $i) {
5427                  !!!cp ('t186');                  !!!cp ('t186');
5428                  !!!parse-error (type => 'unmatched end tag:caption', token => $token);                  !!!parse-error (type => 'unmatched end tag',
5429                                    text => 'caption', token => $token);
5430                  ## Ignore the token                  ## Ignore the token
5431                  !!!next-token;                  !!!next-token;
5432                  next B;                  next B;
# Line 4977  sub _tree_construction_main ($) { Line 5441  sub _tree_construction_main ($) {
5441                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5442                  !!!cp ('t188');                  !!!cp ('t188');
5443                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
5444                                  value => $self->{open_elements}->[-1]->[0]                                  text => $self->{open_elements}->[-1]->[0]
5445                                      ->manakai_local_name,                                      ->manakai_local_name,
5446                                  token => $token);                                  token => $token);
5447                } else {                } else {
# Line 4997  sub _tree_construction_main ($) { Line 5461  sub _tree_construction_main ($) {
5461                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5462                if ($self->{insertion_mode} & BODY_TABLE_IMS) {                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5463                  !!!cp ('t190');                  !!!cp ('t190');
5464                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5465                                    text => $token->{tag_name}, token => $token);
5466                  ## Ignore the token                  ## Ignore the token
5467                  !!!next-token;                  !!!next-token;
5468                  next B;                  next B;
# Line 5011  sub _tree_construction_main ($) { Line 5476  sub _tree_construction_main ($) {
5476                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5477                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5478                !!!cp ('t192');                !!!cp ('t192');
5479                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
5480                                  text => $token->{tag_name}, token => $token);
5481                ## Ignore the token                ## Ignore the token
5482                !!!next-token;                !!!next-token;
5483                next B;                next B;
# Line 5039  sub _tree_construction_main ($) { Line 5505  sub _tree_construction_main ($) {
5505      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
5506        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
5507          if (not $open_tables->[-1]->[1] and # tainted          if (not $open_tables->[-1]->[1] and # tainted
5508              $token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5509            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5510                                
5511            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 5051  sub _tree_construction_main ($) { Line 5517  sub _tree_construction_main ($) {
5517            }            }
5518          }          }
5519    
5520              !!!parse-error (type => 'in table:#character', token => $token);          !!!parse-error (type => 'in table:#text', token => $token);
5521    
5522              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
5523              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
# Line 5102  sub _tree_construction_main ($) { Line 5568  sub _tree_construction_main ($) {
5568          !!!next-token;          !!!next-token;
5569          next B;          next B;
5570        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5571              if ({          if ({
5572                   tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => ($self->{insertion_mode} != IN_ROW_IM),
5573                   th => 1, td => 1,               th => 1, td => 1,
5574                  }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
5575                if ($self->{insertion_mode} == IN_TABLE_IM) {            if ($self->{insertion_mode} == IN_TABLE_IM) {
5576                  ## Clear back to table context              ## Clear back to table context
5577                  while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
5578                                  & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
5579                    !!!cp ('t201');                !!!cp ('t201');
5580                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5581                  }              }
5582                                
5583                  !!!insert-element ('tbody',, $token);              !!!insert-element ('tbody',, $token);
5584                  $self->{insertion_mode} = IN_TABLE_BODY_IM;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5585                  ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
5586                }            }
5587              
5588                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5589                  unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
5590                    !!!cp ('t202');                !!!cp ('t202');
5591                    !!!parse-error (type => 'missing start tag:tr', token => $token);                !!!parse-error (type => 'missing start tag:tr', token => $token);
5592                  }              }
5593                                    
5594                  ## Clear back to table body context              ## Clear back to table body context
5595                  while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
5596                                  & TABLE_ROWS_SCOPING_EL)) {                              & TABLE_ROWS_SCOPING_EL)) {
5597                    !!!cp ('t203');                !!!cp ('t203');
5598                    ## ISSUE: Can this case be reached?                ## ISSUE: Can this case be reached?
5599                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5600                  }              }
5601                                    
5602                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5603                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
# Line 5187  sub _tree_construction_main ($) { Line 5653  sub _tree_construction_main ($) {
5653                  unless (defined $i) {                  unless (defined $i) {
5654                    !!!cp ('t210');                    !!!cp ('t210');
5655  ## TODO: This type is wrong.  ## TODO: This type is wrong.
5656                    !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name}, token => $token);                    !!!parse-error (type => 'unmacthed end tag',
5657                                      text => $token->{tag_name}, token => $token);
5658                    ## Ignore the token                    ## Ignore the token
5659                    !!!nack ('t210.1');                    !!!nack ('t210.1');
5660                    !!!next-token;                    !!!next-token;
# Line 5231  sub _tree_construction_main ($) { Line 5698  sub _tree_construction_main ($) {
5698                  } # INSCOPE                  } # INSCOPE
5699                  unless (defined $i) {                  unless (defined $i) {
5700                    !!!cp ('t216');                    !!!cp ('t216');
5701  ## TODO: This erorr type ios wrong.  ## TODO: This erorr type is wrong.
5702                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                    !!!parse-error (type => 'unmatched end tag',
5703                                      text => $token->{tag_name}, token => $token);
5704                    ## Ignore the token                    ## Ignore the token
5705                    !!!nack ('t216.1');                    !!!nack ('t216.1');
5706                    !!!next-token;                    !!!next-token;
# Line 5307  sub _tree_construction_main ($) { Line 5775  sub _tree_construction_main ($) {
5775                }                }
5776              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5777                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
5778                                value => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
5779                                    ->manakai_local_name,                                    ->manakai_local_name,
5780                                token => $token);                                token => $token);
5781    
# Line 5328  sub _tree_construction_main ($) { Line 5796  sub _tree_construction_main ($) {
5796                unless (defined $i) {                unless (defined $i) {
5797                  !!!cp ('t223');                  !!!cp ('t223');
5798  ## TODO: The following is wrong, maybe.  ## TODO: The following is wrong, maybe.
5799                  !!!parse-error (type => 'unmatched end tag:table', token => $token);                  !!!parse-error (type => 'unmatched end tag', text => 'table',
5800                                    token => $token);
5801                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5802                  !!!nack ('t223.1');                  !!!nack ('t223.1');
5803                  !!!next-token;                  !!!next-token;
5804                  next B;                  next B;
5805                }                }
5806                                
5807  ## TODO: Followings are removed from the latest spec.  ## TODO: Followings are removed from the latest spec.
5808                ## generate implied end tags                ## generate implied end tags
5809                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5810                  !!!cp ('t224');                  !!!cp ('t224');
# Line 5346  sub _tree_construction_main ($) { Line 5815  sub _tree_construction_main ($) {
5815                  !!!cp ('t225');                  !!!cp ('t225');
5816                  ## NOTE: |<table><tr><table>|                  ## NOTE: |<table><tr><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                } else {                } else {
# Line 5387  sub _tree_construction_main ($) { Line 5856  sub _tree_construction_main ($) {
5856                my $type = lc $token->{attributes}->{type}->{value};                my $type = lc $token->{attributes}->{type}->{value};
5857                if ($type eq 'hidden') {                if ($type eq 'hidden') {
5858                  !!!cp ('t227.3');                  !!!cp ('t227.3');
5859                  !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'in table',
5860                                    text => $token->{tag_name}, token => $token);
5861    
5862                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5863    
# Line 5415  sub _tree_construction_main ($) { Line 5885  sub _tree_construction_main ($) {
5885            #            #
5886          }          }
5887    
5888          !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);          !!!parse-error (type => 'in table', text => $token->{tag_name},
5889                            token => $token);
5890    
5891          $insert = $insert_to_foster;          $insert = $insert_to_foster;
5892          #          #
# Line 5437  sub _tree_construction_main ($) { Line 5908  sub _tree_construction_main ($) {
5908                } # INSCOPE                } # INSCOPE
5909                unless (defined $i) {                unless (defined $i) {
5910                  !!!cp ('t230');                  !!!cp ('t230');
5911                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5912                                    text => $token->{tag_name}, token => $token);
5913                  ## Ignore the token                  ## Ignore the token
5914                  !!!nack ('t230.1');                  !!!nack ('t230.1');
5915                  !!!next-token;                  !!!next-token;
# Line 5478  sub _tree_construction_main ($) { Line 5950  sub _tree_construction_main ($) {
5950                  unless (defined $i) {                  unless (defined $i) {
5951                    !!!cp ('t235');                    !!!cp ('t235');
5952  ## TODO: The following is wrong.  ## TODO: The following is wrong.
5953                    !!!parse-error (type => 'unmatched end tag:'.$token->{type}, token => $token);                    !!!parse-error (type => 'unmatched end tag',
5954                                      text => $token->{type}, token => $token);
5955                    ## Ignore the token                    ## Ignore the token
5956                    !!!nack ('t236.1');                    !!!nack ('t236.1');
5957                    !!!next-token;                    !!!next-token;
# Line 5514  sub _tree_construction_main ($) { Line 5987  sub _tree_construction_main ($) {
5987                  } # INSCOPE                  } # INSCOPE
5988                  unless (defined $i) {                  unless (defined $i) {
5989                    !!!cp ('t239');                    !!!cp ('t239');
5990                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                    !!!parse-error (type => 'unmatched end tag',
5991                                      text => $token->{tag_name}, token => $token);
5992                    ## Ignore the token                    ## Ignore the token
5993                    !!!nack ('t239.1');                    !!!nack ('t239.1');
5994                    !!!next-token;                    !!!next-token;
# Line 5560  sub _tree_construction_main ($) { Line 6034  sub _tree_construction_main ($) {
6034                } # INSCOPE                } # INSCOPE
6035                unless (defined $i) {                unless (defined $i) {
6036                  !!!cp ('t243');                  !!!cp ('t243');
6037                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
6038                                    text => $token->{tag_name}, token => $token);
6039                  ## Ignore the token                  ## Ignore the token
6040                  !!!nack ('t243.1');                  !!!nack ('t243.1');
6041                  !!!next-token;                  !!!next-token;
# Line 5594  sub _tree_construction_main ($) { Line 6069  sub _tree_construction_main ($) {
6069                  } # INSCOPE                  } # INSCOPE
6070                    unless (defined $i) {                    unless (defined $i) {
6071                      !!!cp ('t249');                      !!!cp ('t249');
6072                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                      !!!parse-error (type => 'unmatched end tag',
6073                                        text => $token->{tag_name}, token => $token);
6074                      ## Ignore the token                      ## Ignore the token
6075                      !!!nack ('t249.1');                      !!!nack ('t249.1');
6076                      !!!next-token;                      !!!next-token;
# Line 5617  sub _tree_construction_main ($) { Line 6093  sub _tree_construction_main ($) {
6093                  } # INSCOPE                  } # INSCOPE
6094                    unless (defined $i) {                    unless (defined $i) {
6095                      !!!cp ('t252');                      !!!cp ('t252');
6096                      !!!parse-error (type => 'unmatched end tag:tr', token => $token);                      !!!parse-error (type => 'unmatched end tag',
6097                                        text => 'tr', token => $token);
6098                      ## Ignore the token                      ## Ignore the token
6099                      !!!nack ('t252.1');                      !!!nack ('t252.1');
6100                      !!!next-token;                      !!!next-token;
# Line 5652  sub _tree_construction_main ($) { Line 6129  sub _tree_construction_main ($) {
6129                } # INSCOPE                } # INSCOPE
6130                unless (defined $i) {                unless (defined $i) {
6131                  !!!cp ('t256');                  !!!cp ('t256');
6132                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
6133                                    text => $token->{tag_name}, token => $token);
6134                  ## Ignore the token                  ## Ignore the token
6135                  !!!nack ('t256.1');                  !!!nack ('t256.1');
6136                  !!!next-token;                  !!!next-token;
# Line 5679  sub _tree_construction_main ($) { Line 6157  sub _tree_construction_main ($) {
6157                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6158                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6159            !!!cp ('t258');            !!!cp ('t258');
6160            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
6161                              text => $token->{tag_name}, token => $token);
6162            ## Ignore the token            ## Ignore the token
6163            !!!nack ('t258.1');            !!!nack ('t258.1');
6164             !!!next-token;             !!!next-token;
6165            next B;            next B;
6166          } else {          } else {
6167            !!!cp ('t259');            !!!cp ('t259');
6168            !!!parse-error (type => 'in table:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'in table:/',
6169                              text => $token->{tag_name}, token => $token);
6170    
6171            $insert = $insert_to_foster;            $insert = $insert_to_foster;
6172            #            #
# Line 5709  sub _tree_construction_main ($) { Line 6189  sub _tree_construction_main ($) {
6189        }        }
6190      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6191            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6192              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6193                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6194                unless (length $token->{data}) {                unless (length $token->{data}) {
6195                  !!!cp ('t260');                  !!!cp ('t260');
# Line 5736  sub _tree_construction_main ($) { Line 6216  sub _tree_construction_main ($) {
6216              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
6217                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6218                  !!!cp ('t264');                  !!!cp ('t264');
6219                  !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);                  !!!parse-error (type => 'unmatched end tag',
6220                                    text => 'colgroup', token => $token);
6221                  ## Ignore the token                  ## Ignore the token
6222                  !!!next-token;                  !!!next-token;
6223                  next B;                  next B;
# Line 5749  sub _tree_construction_main ($) { Line 6230  sub _tree_construction_main ($) {
6230                }                }
6231              } elsif ($token->{tag_name} eq 'col') {              } elsif ($token->{tag_name} eq 'col') {
6232                !!!cp ('t266');                !!!cp ('t266');
6233                !!!parse-error (type => 'unmatched end tag:col', token => $token);                !!!parse-error (type => 'unmatched end tag',
6234                                  text => 'col', token => $token);
6235                ## Ignore the token                ## Ignore the token
6236                !!!next-token;                !!!next-token;
6237                next B;                next B;
# Line 5779  sub _tree_construction_main ($) { Line 6261  sub _tree_construction_main ($) {
6261            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6262              !!!cp ('t269');              !!!cp ('t269');
6263  ## TODO: Wrong error type?  ## TODO: Wrong error type?
6264              !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);              !!!parse-error (type => 'unmatched end tag',
6265                                text => 'colgroup', token => $token);
6266              ## Ignore the token              ## Ignore the token
6267              !!!nack ('t269.1');              !!!nack ('t269.1');
6268              !!!next-token;              !!!next-token;
# Line 5833  sub _tree_construction_main ($) { Line 6316  sub _tree_construction_main ($) {
6316            !!!nack ('t277.1');            !!!nack ('t277.1');
6317            !!!next-token;            !!!next-token;
6318            next B;            next B;
6319          } elsif ($token->{tag_name} eq 'select' or          } elsif ({
6320                   $token->{tag_name} eq 'input' or                     select => 1, input => 1, textarea => 1,
6321                     }->{$token->{tag_name}} or
6322                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6323                    {                    {
6324                     caption => 1, table => 1,                     caption => 1, table => 1,
# Line 5842  sub _tree_construction_main ($) { Line 6326  sub _tree_construction_main ($) {
6326                     tr => 1, td => 1, th => 1,                     tr => 1, td => 1, th => 1,
6327                    }->{$token->{tag_name}})) {                    }->{$token->{tag_name}})) {
6328            ## TODO: The type below is not good - <select> is replaced by </select>            ## TODO: The type below is not good - <select> is replaced by </select>
6329            !!!parse-error (type => 'not closed:select', token => $token);            !!!parse-error (type => 'not closed', text => 'select',
6330                              token => $token);
6331            ## NOTE: As if the token were </select> (<select> case) or            ## NOTE: As if the token were </select> (<select> case) or
6332            ## as if there were </select> (otherwise).            ## as if there were </select> (otherwise).
6333            ## have an element in table scope            ## have an element in table scope
# Line 5860  sub _tree_construction_main ($) { Line 6345  sub _tree_construction_main ($) {
6345            } # INSCOPE            } # INSCOPE
6346            unless (defined $i) {            unless (defined $i) {
6347              !!!cp ('t280');              !!!cp ('t280');
6348              !!!parse-error (type => 'unmatched end tag:select', token => $token);              !!!parse-error (type => 'unmatched end tag',
6349                                text => 'select', token => $token);
6350              ## Ignore the token              ## Ignore the token
6351              !!!nack ('t280.1');              !!!nack ('t280.1');
6352              !!!next-token;              !!!next-token;
# Line 5884  sub _tree_construction_main ($) { Line 6370  sub _tree_construction_main ($) {
6370            }            }
6371          } else {          } else {
6372            !!!cp ('t282');            !!!cp ('t282');
6373            !!!parse-error (type => 'in select:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'in select',
6374                              text => $token->{tag_name}, token => $token);
6375            ## Ignore the token            ## Ignore the token
6376            !!!nack ('t282.1');            !!!nack ('t282.1');
6377            !!!next-token;            !!!next-token;
# Line 5902  sub _tree_construction_main ($) { Line 6389  sub _tree_construction_main ($) {
6389              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6390            } else {            } else {
6391              !!!cp ('t285');              !!!cp ('t285');
6392              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
6393                                text => $token->{tag_name}, token => $token);
6394              ## Ignore the token              ## Ignore the token
6395            }            }
6396            !!!nack ('t285.1');            !!!nack ('t285.1');
# Line 5914  sub _tree_construction_main ($) { Line 6402  sub _tree_construction_main ($) {
6402              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6403            } else {            } else {
6404              !!!cp ('t287');              !!!cp ('t287');
6405              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
6406                                text => $token->{tag_name}, token => $token);
6407              ## Ignore the token              ## Ignore the token
6408            }            }
6409            !!!nack ('t287.1');            !!!nack ('t287.1');
# Line 5936  sub _tree_construction_main ($) { Line 6425  sub _tree_construction_main ($) {
6425            } # INSCOPE            } # INSCOPE
6426            unless (defined $i) {            unless (defined $i) {
6427              !!!cp ('t290');              !!!cp ('t290');
6428              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
6429                                text => $token->{tag_name}, token => $token);
6430              ## Ignore the token              ## Ignore the token
6431              !!!nack ('t290.1');              !!!nack ('t290.1');
6432              !!!next-token;              !!!next-token;
# Line 5957  sub _tree_construction_main ($) { Line 6447  sub _tree_construction_main ($) {
6447                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6448                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
6449  ## TODO: The following is wrong?  ## TODO: The following is wrong?
6450            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
6451                              text => $token->{tag_name}, token => $token);
6452                                
6453            ## have an element in table scope            ## have an element in table scope
6454            my $i;            my $i;
# Line 5998  sub _tree_construction_main ($) { Line 6489  sub _tree_construction_main ($) {
6489            unless (defined $i) {            unless (defined $i) {
6490              !!!cp ('t297');              !!!cp ('t297');
6491  ## TODO: The following error type is correct?  ## TODO: The following error type is correct?
6492              !!!parse-error (type => 'unmatched end tag:select', token => $token);              !!!parse-error (type => 'unmatched end tag',
6493                                text => 'select', token => $token);
6494              ## Ignore the </select> token              ## Ignore the </select> token
6495              !!!nack ('t297.1');              !!!nack ('t297.1');
6496              !!!next-token; ## TODO: ok?              !!!next-token; ## TODO: ok?
# Line 6015  sub _tree_construction_main ($) { Line 6507  sub _tree_construction_main ($) {
6507            next B;            next B;
6508          } else {          } else {
6509            !!!cp ('t299');            !!!cp ('t299');
6510            !!!parse-error (type => 'in select:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'in select:/',
6511                              text => $token->{tag_name}, token => $token);
6512            ## Ignore the token            ## Ignore the token
6513            !!!nack ('t299.3');            !!!nack ('t299.3');
6514            !!!next-token;            !!!next-token;
# Line 6037  sub _tree_construction_main ($) { Line 6530  sub _tree_construction_main ($) {
6530        }        }
6531      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6532        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6533          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6534            my $data = $1;            my $data = $1;
6535            ## As if in body            ## As if in body
6536            $reconstruct_active_formatting_elements->($insert_to_current);            $reconstruct_active_formatting_elements->($insert_to_current);
# Line 6053  sub _tree_construction_main ($) { Line 6546  sub _tree_construction_main ($) {
6546                    
6547          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6548            !!!cp ('t301');            !!!cp ('t301');
6549            !!!parse-error (type => 'after html:#character', token => $token);            !!!parse-error (type => 'after html:#text', token => $token);
6550              #
           ## Reprocess in the "after body" insertion mode.  
6551          } else {          } else {
6552            !!!cp ('t302');            !!!cp ('t302');
6553              ## "after body" insertion mode
6554              !!!parse-error (type => 'after body:#text', token => $token);
6555              #
6556          }          }
           
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:#character', token => $token);  
6557    
6558          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6559          ## reprocess          ## reprocess
# Line 6069  sub _tree_construction_main ($) { Line 6561  sub _tree_construction_main ($) {
6561        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6562          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6563            !!!cp ('t303');            !!!cp ('t303');
6564            !!!parse-error (type => 'after html:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'after html',
6565                                        text => $token->{tag_name}, token => $token);
6566            ## Reprocess in the "after body" insertion mode.            #
6567          } else {          } else {
6568            !!!cp ('t304');            !!!cp ('t304');
6569              ## "after body" insertion mode
6570              !!!parse-error (type => 'after body',
6571                              text => $token->{tag_name}, token => $token);
6572              #
6573          }          }
6574    
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:'.$token->{tag_name}, token => $token);  
   
6575          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6576          !!!ack-later;          !!!ack-later;
6577          ## reprocess          ## reprocess
# Line 6086  sub _tree_construction_main ($) { Line 6579  sub _tree_construction_main ($) {
6579        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6580          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6581            !!!cp ('t305');            !!!cp ('t305');
6582            !!!parse-error (type => 'after html:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'after html:/',
6583                              text => $token->{tag_name}, token => $token);
6584                        
6585            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6586            ## Reprocess in the "after body" insertion mode.            ## Reprocess.
6587              next B;
6588          } else {          } else {
6589            !!!cp ('t306');            !!!cp ('t306');
6590          }          }
# Line 6098  sub _tree_construction_main ($) { Line 6593  sub _tree_construction_main ($) {
6593          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
6594            if (defined $self->{inner_html_node}) {            if (defined $self->{inner_html_node}) {
6595              !!!cp ('t307');              !!!cp ('t307');
6596              !!!parse-error (type => 'unmatched end tag:html', token => $token);              !!!parse-error (type => 'unmatched end tag',
6597                                text => 'html', token => $token);
6598              ## Ignore the token              ## Ignore the token
6599              !!!next-token;              !!!next-token;
6600              next B;              next B;
# Line 6110  sub _tree_construction_main ($) { Line 6606  sub _tree_construction_main ($) {
6606            }            }
6607          } else {          } else {
6608            !!!cp ('t309');            !!!cp ('t309');
6609            !!!parse-error (type => 'after body:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'after body:/',
6610                              text => $token->{tag_name}, token => $token);
6611    
6612            $self->{insertion_mode} = IN_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6613            ## reprocess            ## reprocess
# Line 6125  sub _tree_construction_main ($) { Line 6622  sub _tree_construction_main ($) {
6622        }        }
6623      } elsif ($self->{insertion_mode} & FRAME_IMS) {      } elsif ($self->{insertion_mode} & FRAME_IMS) {
6624        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6625          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6626            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6627                        
6628            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 6135  sub _tree_construction_main ($) { Line 6632  sub _tree_construction_main ($) {
6632            }            }
6633          }          }
6634                    
6635          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6636            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6637              !!!cp ('t311');              !!!cp ('t311');
6638              !!!parse-error (type => 'in frameset:#character', token => $token);              !!!parse-error (type => 'in frameset:#text', token => $token);
6639            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6640              !!!cp ('t312');              !!!cp ('t312');
6641              !!!parse-error (type => 'after frameset:#character', token => $token);              !!!parse-error (type => 'after frameset:#text', token => $token);
6642            } else { # "after html frameset"            } else { # "after after frameset"
6643              !!!cp ('t313');              !!!cp ('t313');
6644              !!!parse-error (type => 'after html:#character', token => $token);              !!!parse-error (type => 'after html:#text', token => $token);
   
             $self->{insertion_mode} = AFTER_FRAMESET_IM;  
             ## Reprocess in the "after frameset" insertion mode.  
             !!!parse-error (type => 'after frameset:#character', token => $token);  
6645            }            }
6646                        
6647            ## Ignore the token.            ## Ignore the token.
# Line 6164  sub _tree_construction_main ($) { Line 6657  sub _tree_construction_main ($) {
6657                    
6658          die qq[$0: Character "$token->{data}"];          die qq[$0: Character "$token->{data}"];
6659        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
         if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {  
           !!!cp ('t316');  
           !!!parse-error (type => 'after html:'.$token->{tag_name}, token => $token);  
   
           $self->{insertion_mode} = AFTER_FRAMESET_IM;  
           ## Process in the "after frameset" insertion mode.  
         } else {  
           !!!cp ('t317');  
         }  
   
6660          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6661              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6662            !!!cp ('t318');            !!!cp ('t318');
# Line 6191  sub _tree_construction_main ($) { Line 6674  sub _tree_construction_main ($) {
6674            next B;            next B;
6675          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
6676            !!!cp ('t320');            !!!cp ('t320');
6677            ## NOTE: As if in body.            ## NOTE: As if in head.
6678            $parse_rcdata->(CDATA_CONTENT_MODEL);            $parse_rcdata->(CDATA_CONTENT_MODEL);
6679            next B;            next B;
6680    
6681              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6682              ## has no parse error.
6683          } else {          } else {
6684            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6685              !!!cp ('t321');              !!!cp ('t321');
6686              !!!parse-error (type => 'in frameset:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'in frameset',
6687            } else {                              text => $token->{tag_name}, token => $token);
6688              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6689              !!!cp ('t322');              !!!cp ('t322');
6690              !!!parse-error (type => 'after frameset:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'after frameset',
6691                                text => $token->{tag_name}, token => $token);
6692              } else { # "after after frameset"
6693                !!!cp ('t322.2');
6694                !!!parse-error (type => 'after after frameset',
6695                                text => $token->{tag_name}, token => $token);
6696            }            }
6697            ## Ignore the token            ## Ignore the token
6698            !!!nack ('t322.1');            !!!nack ('t322.1');
# Line 6208  sub _tree_construction_main ($) { Line 6700  sub _tree_construction_main ($) {
6700            next B;            next B;
6701          }          }
6702        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
         if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {  
           !!!cp ('t323');  
           !!!parse-error (type => 'after html:/'.$token->{tag_name}, token => $token);  
   
           $self->{insertion_mode} = AFTER_FRAMESET_IM;  
           ## Process in the "after frameset" insertion mode.  
         } else {  
           !!!cp ('t324');  
         }  
   
6703          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6704              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6705            if ($self->{open_elements}->[-1]->[1] & HTML_EL and            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6706                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
6707              !!!cp ('t325');              !!!cp ('t325');
6708              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
6709                                text => $token->{tag_name}, token => $token);
6710              ## Ignore the token              ## Ignore the token
6711              !!!next-token;              !!!next-token;
6712            } else {            } else {
# Line 6249  sub _tree_construction_main ($) { Line 6732  sub _tree_construction_main ($) {
6732          } else {          } else {
6733            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6734              !!!cp ('t330');              !!!cp ('t330');
6735              !!!parse-error (type => 'in frameset:/'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'in frameset:/',
6736            } else {                              text => $token->{tag_name}, token => $token);
6737              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6738                !!!cp ('t330.1');
6739                !!!parse-error (type => 'after frameset:/',
6740                                text => $token->{tag_name}, token => $token);
6741              } else { # "after after html"
6742              !!!cp ('t331');              !!!cp ('t331');
6743              !!!parse-error (type => 'after frameset:/'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'after after frameset:/',
6744                                text => $token->{tag_name}, token => $token);
6745            }            }
6746            ## Ignore the token            ## Ignore the token
6747            !!!next-token;            !!!next-token;
# Line 6319  sub _tree_construction_main ($) { Line 6808  sub _tree_construction_main ($) {
6808                                           ->{has_reference});                                           ->{has_reference});
6809            } elsif ($token->{attributes}->{content}) {            } elsif ($token->{attributes}->{content}) {
6810              if ($token->{attributes}->{content}->{value}              if ($token->{attributes}->{content}->{value}
6811                  =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6812                      [\x09-\x0D\x20]*=                      [\x09\x0A\x0C\x0D\x20]*=
6813                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                      [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6814                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                      ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6815                       /x) {
6816                !!!cp ('t336');                !!!cp ('t336');
6817                ## NOTE: Whether the encoding is supported or not is handled                ## NOTE: Whether the encoding is supported or not is handled
6818                ## in the {change_encoding} callback.                ## in the {change_encoding} callback.
# Line 6360  sub _tree_construction_main ($) { Line 6850  sub _tree_construction_main ($) {
6850          $parse_rcdata->(RCDATA_CONTENT_MODEL);          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6851          next B;          next B;
6852        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
6853          !!!parse-error (type => 'in body:body', token => $token);          !!!parse-error (type => 'in body', text => 'body', token => $token);
6854                                
6855          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
6856              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
# Line 6480  sub _tree_construction_main ($) { Line 6970  sub _tree_construction_main ($) {
6970              if ($i != -1) {              if ($i != -1) {
6971                !!!cp ('t355');                !!!cp ('t355');
6972                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
6973                                value => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
6974                                    ->manakai_local_name,                                    ->manakai_local_name,
6975                                token => $token);                                token => $token);
6976              } else {              } else {
# Line 6634  sub _tree_construction_main ($) { Line 7124  sub _tree_construction_main ($) {
7124                  xmp => 1,                  xmp => 1,
7125                  iframe => 1,                  iframe => 1,
7126                  noembed => 1,                  noembed => 1,
7127                  noframes => 1,                  noframes => 1, ## NOTE: This is an "as if in head" code clone.
7128                  noscript => 0, ## TODO: 1 if scripting is enabled                  noscript => 0, ## TODO: 1 if scripting is enabled
7129                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7130          if ($token->{tag_name} eq 'xmp') {          if ($token->{tag_name} eq 'xmp') {
# Line 6656  sub _tree_construction_main ($) { Line 7146  sub _tree_construction_main ($) {
7146            !!!next-token;            !!!next-token;
7147            next B;            next B;
7148          } else {          } else {
7149              !!!ack ('t391.1');
7150    
7151            my $at = $token->{attributes};            my $at = $token->{attributes};
7152            my $form_attrs;            my $form_attrs;
7153            $form_attrs->{action} = $at->{action} if $at->{action};            $form_attrs->{action} = $at->{action} if $at->{action};
# Line 6699  sub _tree_construction_main ($) { Line 7191  sub _tree_construction_main ($) {
7191                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
7192                          {type => END_TAG_TOKEN, tag_name => 'form',                          {type => END_TAG_TOKEN, tag_name => 'form',
7193                           line => $token->{line}, column => $token->{column}};                           line => $token->{line}, column => $token->{column}};
           !!!nack ('t391.1'); ## NOTE: Not acknowledged.  
7194            !!!back-token (@tokens);            !!!back-token (@tokens);
7195            !!!next-token;            !!!next-token;
7196            next B;            next B;
# Line 6747  sub _tree_construction_main ($) { Line 7238  sub _tree_construction_main ($) {
7238            ## Ignore the token            ## Ignore the token
7239          } else {          } else {
7240            !!!cp ('t398');            !!!cp ('t398');
7241            !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token);            !!!parse-error (type => 'in RCDATA:#eof', token => $token);
7242          }          }
7243          !!!next-token;          !!!next-token;
7244          next B;          next B;
7245          } elsif ($token->{tag_name} eq 'rt' or
7246                   $token->{tag_name} eq 'rp') {
7247            ## has a |ruby| element in scope
7248            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7249              my $node = $self->{open_elements}->[$_];
7250              if ($node->[1] & RUBY_EL) {
7251                !!!cp ('t398.1');
7252                ## generate implied end tags
7253                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7254                  !!!cp ('t398.2');
7255                  pop @{$self->{open_elements}};
7256                }
7257                unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7258                  !!!cp ('t398.3');
7259                  !!!parse-error (type => 'not closed',
7260                                  text => $self->{open_elements}->[-1]->[0]
7261                                      ->manakai_local_name,
7262                                  token => $token);
7263                  pop @{$self->{open_elements}}
7264                      while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
7265                }
7266                last INSCOPE;
7267              } elsif ($node->[1] & SCOPING_EL) {
7268                !!!cp ('t398.4');
7269                last INSCOPE;
7270              }
7271            } # INSCOPE
7272    
7273            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7274    
7275            !!!nack ('t398.5');
7276            !!!next-token;
7277            redo B;
7278        } elsif ($token->{tag_name} eq 'math' or        } elsif ($token->{tag_name} eq 'math' or
7279                 $token->{tag_name} eq 'svg') {                 $token->{tag_name} eq 'svg') {
7280          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7281    
7282            ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7283    
7284          ## "adjust SVG attributes" ('svg' only) - done in insert-element-f          ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7285    
7286          ## "adjust foreign attributes" - done in insert-element-f          ## "adjust foreign attributes" - done in insert-element-f
# Line 6781  sub _tree_construction_main ($) { Line 7307  sub _tree_construction_main ($) {
7307                  thead => 1, tr => 1,                  thead => 1, tr => 1,
7308                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7309          !!!cp ('t401');          !!!cp ('t401');
7310          !!!parse-error (type => 'in body:'.$token->{tag_name}, token => $token);          !!!parse-error (type => 'in body',
7311                            text => $token->{tag_name}, token => $token);
7312          ## Ignore the token          ## Ignore the token
7313          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7314          !!!next-token;          !!!next-token;
# Line 6866  sub _tree_construction_main ($) { Line 7393  sub _tree_construction_main ($) {
7393            }            }
7394    
7395            !!!parse-error (type => 'start tag not allowed',            !!!parse-error (type => 'start tag not allowed',
7396                            value => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
7397            ## NOTE: Ignore the token.            ## NOTE: Ignore the token.
7398            !!!next-token;            !!!next-token;
7399            next B;            next B;
# Line 6876  sub _tree_construction_main ($) { Line 7403  sub _tree_construction_main ($) {
7403            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7404              !!!cp ('t403');              !!!cp ('t403');
7405              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
7406                              value => $_->[0]->manakai_local_name,                              text => $_->[0]->manakai_local_name,
7407                              token => $token);                              token => $token);
7408              last;              last;
7409            } else {            } else {
# Line 6896  sub _tree_construction_main ($) { Line 7423  sub _tree_construction_main ($) {
7423            unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {            unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7424              !!!cp ('t406');              !!!cp ('t406');
7425              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
7426                              value => $self->{open_elements}->[1]->[0]                              text => $self->{open_elements}->[1]->[0]
7427                                  ->manakai_local_name,                                  ->manakai_local_name,
7428                              token => $token);                              token => $token);
7429            } else {            } else {
# Line 6907  sub _tree_construction_main ($) { Line 7434  sub _tree_construction_main ($) {
7434            next B;            next B;
7435          } else {          } else {
7436            !!!cp ('t408');            !!!cp ('t408');
7437            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7438                              text => $token->{tag_name}, token => $token);
7439            ## Ignore the token            ## Ignore the token
7440            !!!next-token;            !!!next-token;
7441            next B;            next B;
# Line 6935  sub _tree_construction_main ($) { Line 7463  sub _tree_construction_main ($) {
7463    
7464          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7465            !!!cp ('t413');            !!!cp ('t413');
7466            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7467                              text => $token->{tag_name}, token => $token);
7468              ## NOTE: Ignore the token.
7469          } else {          } else {
7470            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7471            while ({            while ({
7472                      ## END_TAG_OPTIONAL_EL
7473                    dd => ($token->{tag_name} ne 'dd'),                    dd => ($token->{tag_name} ne 'dd'),
7474                    dt => ($token->{tag_name} ne 'dt'),                    dt => ($token->{tag_name} ne 'dt'),
7475                    li => ($token->{tag_name} ne 'li'),                    li => ($token->{tag_name} ne 'li'),
7476                    p => 1,                    p => 1,
7477                      rt => 1,
7478                      rp => 1,
7479                   }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {                   }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7480              !!!cp ('t409');              !!!cp ('t409');
7481              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6953  sub _tree_construction_main ($) { Line 7486  sub _tree_construction_main ($) {
7486                    ne $token->{tag_name}) {                    ne $token->{tag_name}) {
7487              !!!cp ('t412');              !!!cp ('t412');
7488              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
7489                              value => $self->{open_elements}->[-1]->[0]                              text => $self->{open_elements}->[-1]->[0]
7490                                  ->manakai_local_name,                                  ->manakai_local_name,
7491                              token => $token);                              token => $token);
7492            } else {            } else {
# Line 6990  sub _tree_construction_main ($) { Line 7523  sub _tree_construction_main ($) {
7523    
7524          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7525            !!!cp ('t421');            !!!cp ('t421');
7526            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7527                              text => $token->{tag_name}, token => $token);
7528              ## NOTE: Ignore the token.
7529          } else {          } else {
7530            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7531            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
# Line 7003  sub _tree_construction_main ($) { Line 7538  sub _tree_construction_main ($) {
7538                    ne $token->{tag_name}) {                    ne $token->{tag_name}) {
7539              !!!cp ('t417.1');              !!!cp ('t417.1');
7540              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
7541                              value => $self->{open_elements}->[-1]->[0]                              text => $self->{open_elements}->[-1]->[0]
7542                                  ->manakai_local_name,                                  ->manakai_local_name,
7543                              token => $token);                              token => $token);
7544            } else {            } else {
# Line 7035  sub _tree_construction_main ($) { Line 7570  sub _tree_construction_main ($) {
7570    
7571          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7572            !!!cp ('t425.1');            !!!cp ('t425.1');
7573            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7574                              text => $token->{tag_name}, token => $token);
7575              ## NOTE: Ignore the token.
7576          } else {          } else {
7577            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7578            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
# Line 7047  sub _tree_construction_main ($) { Line 7584  sub _tree_construction_main ($) {
7584            if ($self->{open_elements}->[-1]->[0]->manakai_local_name            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7585                    ne $token->{tag_name}) {                    ne $token->{tag_name}) {
7586              !!!cp ('t425');              !!!cp ('t425');
7587              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
7588                                text => $token->{tag_name}, token => $token);
7589            } else {            } else {
7590              !!!cp ('t426');              !!!cp ('t426');
7591            }            }
# Line 7078  sub _tree_construction_main ($) { Line 7616  sub _tree_construction_main ($) {
7616                    ne $token->{tag_name}) {                    ne $token->{tag_name}) {
7617              !!!cp ('t412.1');              !!!cp ('t412.1');
7618              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
7619                              value => $self->{open_elements}->[-1]->[0]                              text => $self->{open_elements}->[-1]->[0]
7620                                  ->manakai_local_name,                                  ->manakai_local_name,
7621                              token => $token);                              token => $token);
7622            } else {            } else {
# Line 7088  sub _tree_construction_main ($) { Line 7626  sub _tree_construction_main ($) {
7626            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
7627          } else {          } else {
7628            !!!cp ('t413.1');            !!!cp ('t413.1');
7629            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7630                              text => $token->{tag_name}, token => $token);
7631    
7632            !!!cp ('t415.1');            !!!cp ('t415.1');
7633            ## As if <p>, then reprocess the current token            ## As if <p>, then reprocess the current token
# Line 7111  sub _tree_construction_main ($) { Line 7650  sub _tree_construction_main ($) {
7650          next B;          next B;
7651        } elsif ($token->{tag_name} eq 'br') {        } elsif ($token->{tag_name} eq 'br') {
7652          !!!cp ('t428');          !!!cp ('t428');
7653          !!!parse-error (type => 'unmatched end tag:br', token => $token);          !!!parse-error (type => 'unmatched end tag',
7654                            text => 'br', token => $token);
7655    
7656          ## As if <br>          ## As if <br>
7657          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
# Line 7136  sub _tree_construction_main ($) { Line 7676  sub _tree_construction_main ($) {
7676                  noscript => 0, ## TODO: if scripting is enabled                  noscript => 0, ## TODO: if scripting is enabled
7677                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7678          !!!cp ('t429');          !!!cp ('t429');
7679          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);          !!!parse-error (type => 'unmatched end tag',
7680                            text => $token->{tag_name}, token => $token);
7681          ## Ignore the token          ## Ignore the token
7682          !!!next-token;          !!!next-token;
7683          next B;          next B;
# Line 7155  sub _tree_construction_main ($) { Line 7696  sub _tree_construction_main ($) {
7696              ## generate implied end tags              ## generate implied end tags
7697              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7698                !!!cp ('t430');                !!!cp ('t430');
7699                ## ISSUE: Can this case be reached?                ## NOTE: |<ruby><rt></ruby>|.
7700                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7701                  ## which seems wrong.
7702                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
7703                  $node_i++;
7704              }              }
7705                    
7706              ## Step 2              ## Step 2
# Line 7165  sub _tree_construction_main ($) { Line 7709  sub _tree_construction_main ($) {
7709                !!!cp ('t431');                !!!cp ('t431');
7710                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
7711                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
7712                                value => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
7713                                    ->manakai_local_name,                                    ->manakai_local_name,
7714                                token => $token);                                token => $token);
7715              } else {              } else {
# Line 7173  sub _tree_construction_main ($) { Line 7717  sub _tree_construction_main ($) {
7717              }              }
7718                            
7719              ## Step 3              ## Step 3
7720              splice @{$self->{open_elements}}, $node_i;              splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7721    
7722              !!!next-token;              !!!next-token;
7723              last S2;              last S2;
# Line 7184  sub _tree_construction_main ($) { Line 7728  sub _tree_construction_main ($) {
7728                  ($node->[1] & SPECIAL_EL or                  ($node->[1] & SPECIAL_EL or
7729                   $node->[1] & SCOPING_EL)) {                   $node->[1] & SCOPING_EL)) {
7730                !!!cp ('t433');                !!!cp ('t433');
7731                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
7732                                  text => $token->{tag_name}, token => $token);
7733                ## Ignore the token                ## Ignore the token
7734                !!!next-token;                !!!next-token;
7735                last S2;                last S2;
# Line 7230  sub _tree_construction_main ($) { Line 7775  sub _tree_construction_main ($) {
7775    ## TODO: script stuffs    ## TODO: script stuffs
7776  } # _tree_construct_main  } # _tree_construct_main
7777    
7778  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
7779    my $class = shift;    my $class = shift;
7780    my $node = shift;    my $node = shift;
7781    my $s = \$_[0];    #my $s = \$_[0];
7782    my $onerror = $_[1];    my $onerror = $_[1];
7783      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
7784    
7785    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
7786    
# Line 7253  sub set_inner_html ($$$) { Line 7799  sub set_inner_html ($$$) {
7799      }      }
7800    
7801      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
7802      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
7803    } elsif ($nt == 1) {    } elsif ($nt == 1) {
7804      ## TODO: If non-html element      ## TODO: If non-html element
7805    
7806      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
7807    
7808    ## TODO: Support for $get_wrapper
7809    
7810      ## Step 1 # MUST      ## Step 1 # MUST
7811      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
7812      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 7270  sub set_inner_html ($$$) { Line 7818  sub set_inner_html ($$$) {
7818      my $i = 0;      my $i = 0;
7819      $p->{line_prev} = $p->{line} = 1;      $p->{line_prev} = $p->{line} = 1;
7820      $p->{column_prev} = $p->{column} = 0;      $p->{column_prev} = $p->{column} = 0;
7821      $p->{set_next_char} = sub {      require Whatpm::Charset::DecodeHandle;
7822        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
7823        $input = $get_wrapper->($input);
7824        $p->{set_nc} = sub {
7825        my $self = shift;        my $self = shift;
7826    
7827        pop @{$self->{prev_char}};        my $char = '';
7828        unshift @{$self->{prev_char}}, $self->{next_char};        if (defined $self->{next_nc}) {
7829            $char = $self->{next_nc};
7830        $self->{next_char} = -1 and return if $i >= length $$s;          delete $self->{next_nc};
7831        $self->{next_char} = ord substr $$s, $i++, 1;          $self->{nc} = ord $char;
7832          } else {
7833            $self->{char_buffer} = '';
7834            $self->{char_buffer_pos} = 0;
7835            
7836            my $count = $input->manakai_read_until
7837                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
7838                 $self->{char_buffer_pos});
7839            if ($count) {
7840              $self->{line_prev} = $self->{line};
7841              $self->{column_prev} = $self->{column};
7842              $self->{column}++;
7843              $self->{nc}
7844                  = ord substr ($self->{char_buffer},
7845                                $self->{char_buffer_pos}++, 1);
7846              return;
7847            }
7848            
7849            if ($input->read ($char, 1)) {
7850              $self->{nc} = ord $char;
7851            } else {
7852              $self->{nc} = -1;
7853              return;
7854            }
7855          }
7856    
7857        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
7858        $p->{column}++;        $p->{column}++;
7859    
7860        if ($self->{next_char} == 0x000A) { # LF        if ($self->{nc} == 0x000A) { # LF
7861          $p->{line}++;          $p->{line}++;
7862          $p->{column} = 0;          $p->{column} = 0;
7863          !!!cp ('i1');          !!!cp ('i1');
7864        } elsif ($self->{next_char} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
7865          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
7866          $self->{next_char} = 0x000A; # LF # MUST          my $next = '';
7867            if ($input->read ($next, 1) and $next ne "\x0A") {
7868              $self->{next_nc} = $next;
7869            }
7870            $self->{nc} = 0x000A; # LF # MUST
7871          $p->{line}++;          $p->{line}++;
7872          $p->{column} = 0;          $p->{column} = 0;
7873          !!!cp ('i2');          !!!cp ('i2');
7874        } elsif ($self->{next_char} > 0x10FFFF) {        } elsif ($self->{nc} == 0x0000) { # NULL
         $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST  
         !!!cp ('i3');  
       } elsif ($self->{next_char} == 0x0000) { # NULL  
7875          !!!cp ('i4');          !!!cp ('i4');
7876          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
7877          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
       } elsif ($self->{next_char} <= 0x0008 or  
                (0x000E <= $self->{next_char} and  
                 $self->{next_char} <= 0x001F) or  
                (0x007F <= $self->{next_char} and  
                 $self->{next_char} <= 0x009F) or  
                (0xD800 <= $self->{next_char} and  
                 $self->{next_char} <= 0xDFFF) or  
                (0xFDD0 <= $self->{next_char} and  
                 $self->{next_char} <= 0xFDDF) or  
                {  
                 0xFFFE => 1, 0xFFFF => 1, 0x1FFFE => 1, 0x1FFFF => 1,  
                 0x2FFFE => 1, 0x2FFFF => 1, 0x3FFFE => 1, 0x3FFFF => 1,  
                 0x4FFFE => 1, 0x4FFFF => 1, 0x5FFFE => 1, 0x5FFFF => 1,  
                 0x6FFFE => 1, 0x6FFFF => 1, 0x7FFFE => 1, 0x7FFFF => 1,  
                 0x8FFFE => 1, 0x8FFFF => 1, 0x9FFFE => 1, 0x9FFFF => 1,  
                 0xAFFFE => 1, 0xAFFFF => 1, 0xBFFFE => 1, 0xBFFFF => 1,  
                 0xCFFFE => 1, 0xCFFFF => 1, 0xDFFFE => 1, 0xDFFFF => 1,  
                 0xEFFFE => 1, 0xEFFFF => 1, 0xFFFFE => 1, 0xFFFFF => 1,  
                 0x10FFFE => 1, 0x10FFFF => 1,  
                }->{$self->{next_char}}) {  
         !!!cp ('i4.1');  
         !!!parse-error (type => 'control char', level => $self->{must_level});  
 ## TODO: error type documentation  
7878        }        }
7879      };      };
7880      $p->{prev_char} = [-1, -1, -1];  
7881      $p->{next_char} = -1;      $p->{read_until} = sub {
7882              #my ($scalar, $specials_range, $offset) = @_;
7883          return 0 if defined $p->{next_nc};
7884    
7885          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
7886          my $offset = $_[2] || 0;
7887          
7888          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
7889            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
7890            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
7891              substr ($_[0], $offset)
7892                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
7893              my $count = $+[0] - $-[0];
7894              if ($count) {
7895                $p->{column} += $count;
7896                $p->{char_buffer_pos} += $count;
7897                $p->{line_prev} = $p->{line};
7898                $p->{column_prev} = $p->{column} - 1;
7899                $p->{nc} = -1;
7900              }
7901              return $count;
7902            } else {
7903              return 0;
7904            }
7905          } else {
7906            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
7907            if ($count) {
7908              $p->{column} += $count;
7909              $p->{column_prev} += $count;
7910              $p->{nc} = -1;
7911            }
7912            return $count;
7913          }
7914        }; # $p->{read_until}
7915    
7916      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
7917        my (%opt) = @_;        my (%opt) = @_;
7918        my $line = $opt{line};        my $line = $opt{line};
# Line 7341  sub set_inner_html ($$$) { Line 7927  sub set_inner_html ($$$) {
7927        $ponerror->(line => $p->{line}, column => $p->{column}, @_);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
7928      };      };
7929            
7930        my $char_onerror = sub {
7931          my (undef, $type, %opt) = @_;
7932          $ponerror->(layer => 'encode',
7933                      line => $p->{line}, column => $p->{column} + 1,
7934                      %opt, type => $type);
7935        }; # $char_onerror
7936        $input->onerror ($char_onerror);
7937    
7938      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
7939      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
7940    

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24