/[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.162 by wakaba, Thu Sep 11 09:12:27 2008 UTC revision 1.202 by wakaba, Sat Oct 4 14:31:28 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 ('');
# Line 55  sub TABLE_ROWS_EL () { Line 66  sub TABLE_ROWS_EL () {
66  }  }
67    
68  ## NOTE: Used in "generate implied end tags" algorithm.  ## NOTE: Used in "generate implied end tags" algorithm.
69  ## NOTE: There is a code where a modified version of END_TAG_OPTIONAL_EL  ## NOTE: There is a code where a modified version of
70  ## is used in "generate implied end tags" implementation (search for the  ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
71  ## function mae).  ## implementation (search for the algorithm name).
72  sub END_TAG_OPTIONAL_EL () {  sub END_TAG_OPTIONAL_EL () {
73    DD_EL |    DD_EL |
74    DT_EL |    DT_EL |
75    LI_EL |    LI_EL |
76      OPTION_EL |
77      OPTGROUP_EL |
78    P_EL |    P_EL |
79    RUBY_COMPONENT_EL    RUBY_COMPONENT_EL
80  }  }
# Line 73  sub ALL_END_TAG_OPTIONAL_EL () { Line 86  sub ALL_END_TAG_OPTIONAL_EL () {
86    LI_EL |    LI_EL |
87    P_EL |    P_EL |
88    
89      ## ISSUE: option, optgroup, rt, rp?
90    
91    BODY_EL |    BODY_EL |
92    HTML_EL |    HTML_EL |
93    TABLE_CELL_EL |    TABLE_CELL_EL |
# Line 117  sub SPECIAL_EL () { Line 132  sub SPECIAL_EL () {
132    FORM_EL |    FORM_EL |
133    FRAMESET_EL |    FRAMESET_EL |
134    HEADING_EL |    HEADING_EL |
   OPTION_EL |  
   OPTGROUP_EL |  
135    SELECT_EL |    SELECT_EL |
136    TABLE_ROW_EL |    TABLE_ROW_EL |
137    TABLE_ROW_GROUP_EL |    TABLE_ROW_GROUP_EL |
# Line 130  my $el_category = { Line 143  my $el_category = {
143    address => ADDRESS_EL,    address => ADDRESS_EL,
144    applet => MISC_SCOPING_EL,    applet => MISC_SCOPING_EL,
145    area => MISC_SPECIAL_EL,    area => MISC_SPECIAL_EL,
146      article => MISC_SPECIAL_EL,
147      aside => MISC_SPECIAL_EL,
148    b => FORMATTING_EL,    b => FORMATTING_EL,
149    base => MISC_SPECIAL_EL,    base => MISC_SPECIAL_EL,
150    basefont => MISC_SPECIAL_EL,    basefont => MISC_SPECIAL_EL,
# Line 143  my $el_category = { Line 158  my $el_category = {
158    center => MISC_SPECIAL_EL,    center => MISC_SPECIAL_EL,
159    col => MISC_SPECIAL_EL,    col => MISC_SPECIAL_EL,
160    colgroup => MISC_SPECIAL_EL,    colgroup => MISC_SPECIAL_EL,
161      command => MISC_SPECIAL_EL,
162      datagrid => MISC_SPECIAL_EL,
163    dd => DD_EL,    dd => DD_EL,
164      details => MISC_SPECIAL_EL,
165      dialog => MISC_SPECIAL_EL,
166    dir => MISC_SPECIAL_EL,    dir => MISC_SPECIAL_EL,
167    div => DIV_EL,    div => DIV_EL,
168    dl => MISC_SPECIAL_EL,    dl => MISC_SPECIAL_EL,
169    dt => DT_EL,    dt => DT_EL,
170    em => FORMATTING_EL,    em => FORMATTING_EL,
171    embed => MISC_SPECIAL_EL,    embed => MISC_SPECIAL_EL,
172      eventsource => MISC_SPECIAL_EL,
173    fieldset => MISC_SPECIAL_EL,    fieldset => MISC_SPECIAL_EL,
174      figure => MISC_SPECIAL_EL,
175    font => FORMATTING_EL,    font => FORMATTING_EL,
176      footer => MISC_SPECIAL_EL,
177    form => FORM_EL,    form => FORM_EL,
178    frame => MISC_SPECIAL_EL,    frame => MISC_SPECIAL_EL,
179    frameset => FRAMESET_EL,    frameset => FRAMESET_EL,
# Line 162  my $el_category = { Line 184  my $el_category = {
184    h5 => HEADING_EL,    h5 => HEADING_EL,
185    h6 => HEADING_EL,    h6 => HEADING_EL,
186    head => MISC_SPECIAL_EL,    head => MISC_SPECIAL_EL,
187      header => MISC_SPECIAL_EL,
188    hr => MISC_SPECIAL_EL,    hr => MISC_SPECIAL_EL,
189    html => HTML_EL,    html => HTML_EL,
190    i => FORMATTING_EL,    i => FORMATTING_EL,
191    iframe => MISC_SPECIAL_EL,    iframe => MISC_SPECIAL_EL,
192    img => MISC_SPECIAL_EL,    img => MISC_SPECIAL_EL,
193      #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
194    input => MISC_SPECIAL_EL,    input => MISC_SPECIAL_EL,
195    isindex => MISC_SPECIAL_EL,    isindex => MISC_SPECIAL_EL,
196    li => LI_EL,    li => LI_EL,
# Line 175  my $el_category = { Line 199  my $el_category = {
199    marquee => MISC_SCOPING_EL,    marquee => MISC_SCOPING_EL,
200    menu => MISC_SPECIAL_EL,    menu => MISC_SPECIAL_EL,
201    meta => MISC_SPECIAL_EL,    meta => MISC_SPECIAL_EL,
202      nav => MISC_SPECIAL_EL,
203    nobr => NOBR_EL | FORMATTING_EL,    nobr => NOBR_EL | FORMATTING_EL,
204    noembed => MISC_SPECIAL_EL,    noembed => MISC_SPECIAL_EL,
205    noframes => MISC_SPECIAL_EL,    noframes => MISC_SPECIAL_EL,
# Line 193  my $el_category = { Line 218  my $el_category = {
218    s => FORMATTING_EL,    s => FORMATTING_EL,
219    script => MISC_SPECIAL_EL,    script => MISC_SPECIAL_EL,
220    select => SELECT_EL,    select => SELECT_EL,
221      section => MISC_SPECIAL_EL,
222    small => FORMATTING_EL,    small => FORMATTING_EL,
223    spacer => MISC_SPECIAL_EL,    spacer => MISC_SPECIAL_EL,
224    strike => FORMATTING_EL,    strike => FORMATTING_EL,
# Line 223  my $el_category_f = { Line 249  my $el_category_f = {
249      mtext => FOREIGN_FLOW_CONTENT_EL,      mtext => FOREIGN_FLOW_CONTENT_EL,
250    },    },
251    $SVG_NS => {    $SVG_NS => {
252      foreignObject => FOREIGN_FLOW_CONTENT_EL,      foreignObject => FOREIGN_FLOW_CONTENT_EL | MISC_SCOPING_EL,
253      desc => FOREIGN_FLOW_CONTENT_EL,      desc => FOREIGN_FLOW_CONTENT_EL,
254      title => FOREIGN_FLOW_CONTENT_EL,      title => FOREIGN_FLOW_CONTENT_EL,
255    },    },
# Line 312  my $foreign_attr_xname = { Line 338  my $foreign_attr_xname = {
338    
339  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
340    
341  my $c1_entity_char = {  my $charref_map = {
342      0x0D => 0x000A,
343    0x80 => 0x20AC,    0x80 => 0x20AC,
344    0x81 => 0xFFFD,    0x81 => 0xFFFD,
345    0x82 => 0x201A,    0x82 => 0x201A,
# Line 345  my $c1_entity_char = { Line 372  my $c1_entity_char = {
372    0x9D => 0xFFFD,    0x9D => 0xFFFD,
373    0x9E => 0x017E,    0x9E => 0x017E,
374    0x9F => 0x0178,    0x9F => 0x0178,
375  }; # $c1_entity_char  }; # $charref_map
376    $charref_map->{$_} = 0xFFFD
377        for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
378            0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
379            0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
380            0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
381            0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
382            0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
383            0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
384    
385    ## TODO: Invoke the reset algorithm when a resettable element is
386    ## created (cf. HTML5 revision 2259).
387    
388  sub parse_byte_string ($$$$;$) {  sub parse_byte_string ($$$$;$) {
389    my $self = shift;    my $self = shift;
# Line 390  sub parse_byte_stream ($$$$;$$) { Line 428  sub parse_byte_stream ($$$$;$$) {
428            ## TODO: Is this ok?  Transfer protocol's parameter should be            ## TODO: Is this ok?  Transfer protocol's parameter should be
429            ## interpreted in its semantics?            ## interpreted in its semantics?
430    
       ## ISSUE: Unsupported encoding is not ignored according to the spec.  
431        ($char_stream, $e_status) = $charset->get_decode_handle        ($char_stream, $e_status) = $charset->get_decode_handle
432            ($byte_stream, allow_error_reporting => 1,            ($byte_stream, allow_error_reporting => 1,
433             allow_fallback => 1);             allow_fallback => 1);
# Line 398  sub parse_byte_stream ($$$$;$$) { Line 435  sub parse_byte_stream ($$$$;$$) {
435          $self->{confident} = 1;          $self->{confident} = 1;
436          last SNIFFING;          last SNIFFING;
437        } else {        } else {
438          ## TODO: unsupported error          !!!parse-error (type => 'charset:not supported',
439                            layer => 'encode',
440                            line => 1, column => 1,
441                            value => $charset_name,
442                            level => $self->{level}->{uncertain});
443        }        }
444      }      }
445    
# Line 447  sub parse_byte_stream ($$$$;$$) { Line 488  sub parse_byte_stream ($$$$;$$) {
488      if (defined $charset_name) {      if (defined $charset_name) {
489        $charset = Message::Charset::Info->get_by_html_name ($charset_name);        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
490    
       ## ISSUE: Unsupported encoding is not ignored according to the spec.  
491        require Whatpm::Charset::DecodeHandle;        require Whatpm::Charset::DecodeHandle;
492        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
493            ($byte_stream);            ($byte_stream);
# Line 496  sub parse_byte_stream ($$$$;$$) { Line 536  sub parse_byte_stream ($$$$;$$) {
536                      line => 1, column => 1,                      line => 1, column => 1,
537                      layer => 'encode');                      layer => 'encode');
538    } elsif (not ($e_status &    } elsif (not ($e_status &
539                  Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL())) {                  Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
540      $self->{input_encoding} = $charset->get_iana_name;      $self->{input_encoding} = $charset->get_iana_name;
541      !!!parse-error (type => 'chardecode:no error',      !!!parse-error (type => 'chardecode:no error',
542                      text => $self->{input_encoding},                      text => $self->{input_encoding},
# Line 561  sub parse_byte_stream ($$$$;$$) { Line 601  sub parse_byte_stream ($$$$;$$) {
601    my $char_onerror = sub {    my $char_onerror = sub {
602      my (undef, $type, %opt) = @_;      my (undef, $type, %opt) = @_;
603      !!!parse-error (layer => 'encode',      !!!parse-error (layer => 'encode',
604                      %opt, type => $type,                      line => $self->{line}, column => $self->{column} + 1,
605                      line => $self->{line}, column => $self->{column} + 1);                      %opt, type => $type);
606      if ($opt{octets}) {      if ($opt{octets}) {
607        ${$opt{octets}} = "\x{FFFD}"; # relacement character        ${$opt{octets}} = "\x{FFFD}"; # relacement character
608      }      }
# Line 571  sub parse_byte_stream ($$$$;$$) { Line 611  sub parse_byte_stream ($$$$;$$) {
611    my $wrapped_char_stream = $get_wrapper->($char_stream);    my $wrapped_char_stream = $get_wrapper->($char_stream);
612    $wrapped_char_stream->onerror ($char_onerror);    $wrapped_char_stream->onerror ($char_onerror);
613    
614    my @args = @_; shift @args; # $s    my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
615    my $return;    my $return;
616    try {    try {
617      $return = $self->parse_char_stream ($wrapped_char_stream, @args);        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
# Line 586  sub parse_byte_stream ($$$$;$$) { Line 626  sub parse_byte_stream ($$$$;$$) {
626                        line => 1, column => 1,                        line => 1, column => 1,
627                        layer => 'encode');                        layer => 'encode');
628      } elsif (not ($e_status &      } elsif (not ($e_status &
629                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL())) {                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
630        $self->{input_encoding} = $charset->get_iana_name;        $self->{input_encoding} = $charset->get_iana_name;
631        !!!parse-error (type => 'chardecode:no error',        !!!parse-error (type => 'chardecode:no error',
632                        text => $self->{input_encoding},                        text => $self->{input_encoding},
# Line 618  sub parse_byte_stream ($$$$;$$) { Line 658  sub parse_byte_stream ($$$$;$$) {
658  sub parse_char_string ($$$;$$) {  sub parse_char_string ($$$;$$) {
659    #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;    #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
660    my $self = shift;    my $self = shift;
   require utf8;  
661    my $s = ref $_[0] ? $_[0] : \($_[0]);    my $s = ref $_[0] ? $_[0] : \($_[0]);
662    open my $input, '<' . (utf8::is_utf8 ($$s) ? ':utf8' : ''), $s;    require Whatpm::Charset::DecodeHandle;
663    if ($_[3]) {    my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
     $input = $_[3]->($input);  
   }  
664    return $self->parse_char_stream ($input, @_[1..$#_]);    return $self->parse_char_stream ($input, @_[1..$#_]);
665  } # parse_char_string  } # parse_char_string
666  *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.  *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
667    
668  sub parse_char_stream ($$$;$) {  sub parse_char_stream ($$$;$$) {
669    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
670    my $input = $_[0];    my $input = $_[0];
671    $self->{document} = $_[1];    $self->{document} = $_[1];
# Line 639  sub parse_char_stream ($$$;$) { Line 676  sub parse_char_stream ($$$;$) {
676    $self->{confident} = 1 unless exists $self->{confident};    $self->{confident} = 1 unless exists $self->{confident};
677    $self->{document}->input_encoding ($self->{input_encoding})    $self->{document}->input_encoding ($self->{input_encoding})
678        if defined $self->{input_encoding};        if defined $self->{input_encoding};
679    ## TODO: |{input_encoding}| is needless?
680    
   my $i = 0;  
681    $self->{line_prev} = $self->{line} = 1;    $self->{line_prev} = $self->{line} = 1;
682    $self->{column_prev} = $self->{column} = 0;    $self->{column_prev} = -1;
683    $self->{set_next_char} = sub {    $self->{column} = 0;
684      $self->{set_nc} = sub {
685      my $self = shift;      my $self = shift;
686    
687      pop @{$self->{prev_char}};      my $char = '';
688      unshift @{$self->{prev_char}}, $self->{next_char};      if (defined $self->{next_nc}) {
689          $char = $self->{next_nc};
690      my $char;        delete $self->{next_nc};
691      if (defined $self->{next_next_char}) {        $self->{nc} = ord $char;
       $char = $self->{next_next_char};  
       delete $self->{next_next_char};  
692      } else {      } else {
693        $char = $input->getc;        $self->{char_buffer} = '';
694          $self->{char_buffer_pos} = 0;
695    
696          my $count = $input->manakai_read_until
697             ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
698          if ($count) {
699            $self->{line_prev} = $self->{line};
700            $self->{column_prev} = $self->{column};
701            $self->{column}++;
702            $self->{nc}
703                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
704            return;
705          }
706    
707          if ($input->read ($char, 1)) {
708            $self->{nc} = ord $char;
709          } else {
710            $self->{nc} = -1;
711            return;
712          }
713      }      }
     $self->{next_char} = -1 and return unless defined $char;  
     $self->{next_char} = ord $char;  
714    
715      ($self->{line_prev}, $self->{column_prev})      ($self->{line_prev}, $self->{column_prev})
716          = ($self->{line}, $self->{column});          = ($self->{line}, $self->{column});
717      $self->{column}++;      $self->{column}++;
718            
719      if ($self->{next_char} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
720        !!!cp ('j1');        !!!cp ('j1');
721        $self->{line}++;        $self->{line}++;
722        $self->{column} = 0;        $self->{column} = 0;
723      } elsif ($self->{next_char} == 0x000D) { # CR      } elsif ($self->{nc} == 0x000D) { # CR
724        !!!cp ('j2');        !!!cp ('j2');
725        my $next = $input->getc;  ## TODO: support for abort/streaming
726        if (defined $next and $next ne "\x0A") {        my $next = '';
727          $self->{next_next_char} = $next;        if ($input->read ($next, 1) and $next ne "\x0A") {
728            $self->{next_nc} = $next;
729        }        }
730        $self->{next_char} = 0x000A; # LF # MUST        $self->{nc} = 0x000A; # LF # MUST
731        $self->{line}++;        $self->{line}++;
732        $self->{column} = 0;        $self->{column} = 0;
733      } 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  
734        !!!cp ('j4');        !!!cp ('j4');
735        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
736        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
737      } elsif ($self->{next_char} <= 0x0008 or      }
738               (0x000E <= $self->{next_char} and $self->{next_char} <= 0x001F) or    };
739               (0x007F <= $self->{next_char} and $self->{next_char} <= 0x009F) or  
740               (0xD800 <= $self->{next_char} and $self->{next_char} <= 0xDFFF) or    $self->{read_until} = sub {
741               (0xFDD0 <= $self->{next_char} and $self->{next_char} <= 0xFDDF) or      #my ($scalar, $specials_range, $offset) = @_;
742               {      return 0 if defined $self->{next_nc};
743                0xFFFE => 1, 0xFFFF => 1, 0x1FFFE => 1, 0x1FFFF => 1,  
744                0x2FFFE => 1, 0x2FFFF => 1, 0x3FFFE => 1, 0x3FFFF => 1,      my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
745                0x4FFFE => 1, 0x4FFFF => 1, 0x5FFFE => 1, 0x5FFFF => 1,      my $offset = $_[2] || 0;
746                0x6FFFE => 1, 0x6FFFF => 1, 0x7FFFE => 1, 0x7FFFF => 1,  
747                0x8FFFE => 1, 0x8FFFF => 1, 0x9FFFE => 1, 0x9FFFF => 1,      if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
748                0xAFFFE => 1, 0xAFFFF => 1, 0xBFFFE => 1, 0xBFFFF => 1,        pos ($self->{char_buffer}) = $self->{char_buffer_pos};
749                0xCFFFE => 1, 0xCFFFF => 1, 0xDFFFE => 1, 0xDFFFF => 1,        if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
750                0xEFFFE => 1, 0xEFFFF => 1, 0xFFFFE => 1, 0xFFFFF => 1,          substr ($_[0], $offset)
751                0x10FFFE => 1, 0x10FFFF => 1,              = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
752               }->{$self->{next_char}}) {          my $count = $+[0] - $-[0];
753        !!!cp ('j5');          if ($count) {
754        if ($self->{next_char} < 0x10000) {            $self->{column} += $count;
755          !!!parse-error (type => 'control char',            $self->{char_buffer_pos} += $count;
756                          text => (sprintf 'U+%04X', $self->{next_char}));            $self->{line_prev} = $self->{line};
757              $self->{column_prev} = $self->{column} - 1;
758              $self->{nc} = -1;
759            }
760            return $count;
761        } else {        } else {
762          !!!parse-error (type => 'control char',          return 0;
763                          text => (sprintf 'U-%08X', $self->{next_char}));        }
764        } else {
765          my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
766          if ($count) {
767            $self->{column} += $count;
768            $self->{line_prev} = $self->{line};
769            $self->{column_prev} = $self->{column} - 1;
770            $self->{nc} = -1;
771        }        }
772          return $count;
773      }      }
774    };    }; # $self->{read_until}
   $self->{prev_char} = [-1, -1, -1];  
   $self->{next_char} = -1;  
775    
776    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
777      my (%opt) = @_;      my (%opt) = @_;
# Line 722  sub parse_char_stream ($$$;$) { Line 783  sub parse_char_stream ($$$;$) {
783      $onerror->(line => $self->{line}, column => $self->{column}, @_);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
784    };    };
785    
786      my $char_onerror = sub {
787        my (undef, $type, %opt) = @_;
788        !!!parse-error (layer => 'encode',
789                        line => $self->{line}, column => $self->{column} + 1,
790                        %opt, type => $type);
791      }; # $char_onerror
792    
793      if ($_[3]) {
794        $input = $_[3]->($input);
795        $input->onerror ($char_onerror);
796      } else {
797        $input->onerror ($char_onerror) unless defined $input->onerror;
798      }
799    
800    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
801    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
802    $self->_construct_tree;    $self->_construct_tree;
# Line 741  sub new ($) { Line 816  sub new ($) {
816                info => 'i',                info => 'i',
817                uncertain => 'u'},                uncertain => 'u'},
818    }, $class;    }, $class;
819    $self->{set_next_char} = sub {    $self->{set_nc} = sub {
820      $self->{next_char} = -1;      $self->{nc} = -1;
821    };    };
822    $self->{parse_error} = sub {    $self->{parse_error} = sub {
823      #      #
# Line 769  sub RCDATA_CONTENT_MODEL () { CM_ENTITY Line 844  sub RCDATA_CONTENT_MODEL () { CM_ENTITY
844  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
845    
846  sub DATA_STATE () { 0 }  sub DATA_STATE () { 0 }
847  sub ENTITY_DATA_STATE () { 1 }  #sub ENTITY_DATA_STATE () { 1 }
848  sub TAG_OPEN_STATE () { 2 }  sub TAG_OPEN_STATE () { 2 }
849  sub CLOSE_TAG_OPEN_STATE () { 3 }  sub CLOSE_TAG_OPEN_STATE () { 3 }
850  sub TAG_NAME_STATE () { 4 }  sub TAG_NAME_STATE () { 4 }
# Line 780  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 Line 855  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8
855  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
856  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
857  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
858  sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }  #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
859  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
860  sub COMMENT_START_STATE () { 14 }  sub COMMENT_START_STATE () { 14 }
861  sub COMMENT_START_DASH_STATE () { 15 }  sub COMMENT_START_DASH_STATE () { 15 }
# Line 803  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STAT Line 878  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STAT
878  sub BOGUS_DOCTYPE_STATE () { 32 }  sub BOGUS_DOCTYPE_STATE () { 32 }
879  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
880  sub SELF_CLOSING_START_TAG_STATE () { 34 }  sub SELF_CLOSING_START_TAG_STATE () { 34 }
881  sub CDATA_BLOCK_STATE () { 35 }  sub CDATA_SECTION_STATE () { 35 }
882    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
883    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
884    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
885    sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
886    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
887    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
888    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
889    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
890    ## NOTE: "Entity data state", "entity in attribute value state", and
891    ## "consume a character reference" algorithm are jointly implemented
892    ## using the following six states:
893    sub ENTITY_STATE () { 44 }
894    sub ENTITY_HASH_STATE () { 45 }
895    sub NCR_NUM_STATE () { 46 }
896    sub HEXREF_X_STATE () { 47 }
897    sub HEXREF_HEX_STATE () { 48 }
898    sub ENTITY_NAME_STATE () { 49 }
899    sub PCDATA_STATE () { 50 } # "data state" in the spec
900    
901  sub DOCTYPE_TOKEN () { 1 }  sub DOCTYPE_TOKEN () { 1 }
902  sub COMMENT_TOKEN () { 2 }  sub COMMENT_TOKEN () { 2 }
# Line 856  sub IN_COLUMN_GROUP_IM () { 0b10 } Line 949  sub IN_COLUMN_GROUP_IM () { 0b10 }
949  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
950    my $self = shift;    my $self = shift;
951    $self->{state} = DATA_STATE; # MUST    $self->{state} = DATA_STATE; # MUST
952      #$self->{s_kwd}; # state keyword - initialized when used
953      #$self->{entity__value}; # initialized when used
954      #$self->{entity__match}; # initialized when used
955    $self->{content_model} = PCDATA_CONTENT_MODEL; # be    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
956    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    undef $self->{ct}; # current token
957    undef $self->{current_attribute};    undef $self->{ca}; # current attribute
958    undef $self->{last_emitted_start_tag_name};    undef $self->{last_stag_name}; # last emitted start tag name
959    undef $self->{last_attribute_value_state};    #$self->{prev_state}; # initialized when used
960    delete $self->{self_closing};    delete $self->{self_closing};
961    $self->{char} = [];    $self->{char_buffer} = '';
962    # $self->{next_char}    $self->{char_buffer_pos} = 0;
963      $self->{nc} = -1; # next input character
964      #$self->{next_nc}
965    !!!next-input-character;    !!!next-input-character;
966    $self->{token} = [];    $self->{token} = [];
967    # $self->{escape}    # $self->{escape}
# Line 874  sub _initialize_tokenizer ($) { Line 972  sub _initialize_tokenizer ($) {
972  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
973  ##   ->{name} (DOCTYPE_TOKEN)  ##   ->{name} (DOCTYPE_TOKEN)
974  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
975  ##   ->{public_identifier} (DOCTYPE_TOKEN)  ##   ->{pubid} (DOCTYPE_TOKEN)
976  ##   ->{system_identifier} (DOCTYPE_TOKEN)  ##   ->{sysid} (DOCTYPE_TOKEN)
977  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
978  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
979  ##        ->{name}  ##        ->{name}
# Line 894  sub _initialize_tokenizer ($) { Line 992  sub _initialize_tokenizer ($) {
992  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
993  ## and removed from the list.  ## and removed from the list.
994    
995  ## NOTE: HTML5 "Writing HTML documents" section, applied to  ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
996  ## documents and not to user agents and conformance checkers,  ## (This requirement was dropped from HTML5 spec, unfortunately.)
997  ## contains some requirements that are not detected by the  
998  ## parsing algorithm:  my $is_space = {
999  ## - Some requirements on character encoding declarations. ## TODO    0x0009 => 1, # CHARACTER TABULATION (HT)
1000  ## - "Elements MUST NOT contain content that their content model disallows."    0x000A => 1, # LINE FEED (LF)
1001  ##   ... Some are parse error, some are not (will be reported by c.c.).    #0x000B => 0, # LINE TABULATION (VT)
1002  ## - Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) ## TODO    0x000C => 1, # FORM FEED (FF)
1003  ## - Text (in elements, attributes, and comments) SHOULD NOT contain    #0x000D => 1, # CARRIAGE RETURN (CR)
1004  ##   control characters other than space characters. ## TODO: (what is control character? C0, C1 and DEL?  Unicode control character?)    0x0020 => 1, # SPACE (SP)
1005    };
 ## TODO: HTML5 poses authors two SHOULD-level requirements that cannot  
 ## be detected by the HTML5 parsing algorithm:  
 ## - Text,  
1006    
1007  sub _get_next_token ($) {  sub _get_next_token ($) {
1008    my $self = shift;    my $self = shift;
1009    
1010    if ($self->{self_closing}) {    if ($self->{self_closing}) {
1011      !!!parse-error (type => 'nestc', token => $self->{current_token});      !!!parse-error (type => 'nestc', token => $self->{ct});
1012      ## NOTE: The |self_closing| flag is only set by start tag token.      ## NOTE: The |self_closing| flag is only set by start tag token.
1013      ## 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
1014      ## |current_token|.      ## |ct|.
1015      delete $self->{self_closing};      delete $self->{self_closing};
1016    }    }
1017    
# Line 926  sub _get_next_token ($) { Line 1021  sub _get_next_token ($) {
1021    }    }
1022    
1023    A: {    A: {
1024      if ($self->{state} == DATA_STATE) {      if ($self->{state} == PCDATA_STATE) {
1025        if ($self->{next_char} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1026    
1027          if ($self->{nc} == 0x0026) { # &
1028            !!!cp (0.1);
1029            ## NOTE: In the spec, the tokenizer is switched to the
1030            ## "entity data state".  In this implementation, the tokenizer
1031            ## is switched to the |ENTITY_STATE|, which is an implementation
1032            ## of the "consume a character reference" algorithm.
1033            $self->{entity_add} = -1;
1034            $self->{prev_state} = DATA_STATE;
1035            $self->{state} = ENTITY_STATE;
1036            !!!next-input-character;
1037            redo A;
1038          } elsif ($self->{nc} == 0x003C) { # <
1039            !!!cp (0.2);
1040            $self->{state} = TAG_OPEN_STATE;
1041            !!!next-input-character;
1042            redo A;
1043          } elsif ($self->{nc} == -1) {
1044            !!!cp (0.3);
1045            !!!emit ({type => END_OF_FILE_TOKEN,
1046                      line => $self->{line}, column => $self->{column}});
1047            last A; ## TODO: ok?
1048          } else {
1049            !!!cp (0.4);
1050            #
1051          }
1052    
1053          # Anything else
1054          my $token = {type => CHARACTER_TOKEN,
1055                       data => chr $self->{nc},
1056                       line => $self->{line}, column => $self->{column},
1057                      };
1058          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1059    
1060          ## Stay in the state.
1061          !!!next-input-character;
1062          !!!emit ($token);
1063          redo A;
1064        } elsif ($self->{state} == DATA_STATE) {
1065          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1066          if ($self->{nc} == 0x0026) { # &
1067            $self->{s_kwd} = '';
1068          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1069              not $self->{escape}) {              not $self->{escape}) {
1070            !!!cp (1);            !!!cp (1);
1071            $self->{state} = ENTITY_DATA_STATE;            ## NOTE: In the spec, the tokenizer is switched to the
1072              ## "entity data state".  In this implementation, the tokenizer
1073              ## is switched to the |ENTITY_STATE|, which is an implementation
1074              ## of the "consume a character reference" algorithm.
1075              $self->{entity_add} = -1;
1076              $self->{prev_state} = DATA_STATE;
1077              $self->{state} = ENTITY_STATE;
1078            !!!next-input-character;            !!!next-input-character;
1079            redo A;            redo A;
1080          } else {          } else {
1081            !!!cp (2);            !!!cp (2);
1082            #            #
1083          }          }
1084        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
1085          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1086            unless ($self->{escape}) {            $self->{s_kwd} .= '-';
1087              if ($self->{prev_char}->[0] == 0x002D and # -            
1088                  $self->{prev_char}->[1] == 0x0021 and # !            if ($self->{s_kwd} eq '<!--') {
1089                  $self->{prev_char}->[2] == 0x003C) { # <              !!!cp (3);
1090                !!!cp (3);              $self->{escape} = 1; # unless $self->{escape};
1091                $self->{escape} = 1;              $self->{s_kwd} = '--';
1092              } else {              #
1093                !!!cp (4);            } elsif ($self->{s_kwd} eq '---') {
1094              }              !!!cp (4);
1095                $self->{s_kwd} = '--';
1096                #
1097            } else {            } else {
1098              !!!cp (5);              !!!cp (5);
1099                #
1100            }            }
1101          }          }
1102                    
1103          #          #
1104        } elsif ($self->{next_char} == 0x003C) { # <        } elsif ($self->{nc} == 0x0021) { # !
1105            if (length $self->{s_kwd}) {
1106              !!!cp (5.1);
1107              $self->{s_kwd} .= '!';
1108              #
1109            } else {
1110              !!!cp (5.2);
1111              #$self->{s_kwd} = '';
1112              #
1113            }
1114            #
1115          } elsif ($self->{nc} == 0x003C) { # <
1116          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1117              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1118               not $self->{escape})) {               not $self->{escape})) {
# Line 965  sub _get_next_token ($) { Line 1122  sub _get_next_token ($) {
1122            redo A;            redo A;
1123          } else {          } else {
1124            !!!cp (7);            !!!cp (7);
1125              $self->{s_kwd} = '';
1126            #            #
1127          }          }
1128        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1129          if ($self->{escape} and          if ($self->{escape} and
1130              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1131            if ($self->{prev_char}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '--') {
               $self->{prev_char}->[1] == 0x002D) { # -  
1132              !!!cp (8);              !!!cp (8);
1133              delete $self->{escape};              delete $self->{escape};
1134            } else {            } else {
# Line 981  sub _get_next_token ($) { Line 1138  sub _get_next_token ($) {
1138            !!!cp (10);            !!!cp (10);
1139          }          }
1140                    
1141            $self->{s_kwd} = '';
1142          #          #
1143        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1144          !!!cp (11);          !!!cp (11);
1145            $self->{s_kwd} = '';
1146          !!!emit ({type => END_OF_FILE_TOKEN,          !!!emit ({type => END_OF_FILE_TOKEN,
1147                    line => $self->{line}, column => $self->{column}});                    line => $self->{line}, column => $self->{column}});
1148          last A; ## TODO: ok?          last A; ## TODO: ok?
1149        } else {        } else {
1150          !!!cp (12);          !!!cp (12);
1151            $self->{s_kwd} = '';
1152            #
1153        }        }
1154    
1155        # Anything else        # Anything else
1156        my $token = {type => CHARACTER_TOKEN,        my $token = {type => CHARACTER_TOKEN,
1157                     data => chr $self->{next_char},                     data => chr $self->{nc},
1158                     line => $self->{line}, column => $self->{column},                     line => $self->{line}, column => $self->{column},
1159                    };                    };
1160        ## Stay in the data state        if ($self->{read_until}->($token->{data}, q[-!<>&],
1161        !!!next-input-character;                                  length $token->{data})) {
1162            $self->{s_kwd} = '';
1163        !!!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  
1164    
1165        unless (defined $token) {        ## Stay in the data state.
1166          if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1167          !!!cp (13);          !!!cp (13);
1168          !!!emit ({type => CHARACTER_TOKEN, data => '&',          $self->{state} = PCDATA_STATE;
                   line => $l, column => $c,  
                  });  
1169        } else {        } else {
1170          !!!cp (14);          !!!cp (14);
1171          !!!emit ($token);          ## Stay in the state.
1172        }        }
1173          !!!next-input-character;
1174          !!!emit ($token);
1175        redo A;        redo A;
1176      } elsif ($self->{state} == TAG_OPEN_STATE) {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1177        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1178          if ($self->{next_char} == 0x002F) { # /          if ($self->{nc} == 0x002F) { # /
1179            !!!cp (15);            !!!cp (15);
1180            !!!next-input-character;            !!!next-input-character;
1181            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1182            redo A;            redo A;
1183            } elsif ($self->{nc} == 0x0021) { # !
1184              !!!cp (15.1);
1185              $self->{s_kwd} = '<' unless $self->{escape};
1186              #
1187          } else {          } else {
1188            !!!cp (16);            !!!cp (16);
1189            ## reconsume            #
           $self->{state} = DATA_STATE;  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev},  
                    });  
   
           redo A;  
1190          }          }
1191    
1192            ## reconsume
1193            $self->{state} = DATA_STATE;
1194            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1195                      line => $self->{line_prev},
1196                      column => $self->{column_prev},
1197                     });
1198            redo A;
1199        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1200          if ($self->{next_char} == 0x0021) { # !          if ($self->{nc} == 0x0021) { # !
1201            !!!cp (17);            !!!cp (17);
1202            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1203            !!!next-input-character;            !!!next-input-character;
1204            redo A;            redo A;
1205          } elsif ($self->{next_char} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1206            !!!cp (18);            !!!cp (18);
1207            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1208            !!!next-input-character;            !!!next-input-character;
1209            redo A;            redo A;
1210          } elsif (0x0041 <= $self->{next_char} and          } elsif (0x0041 <= $self->{nc} and
1211                   $self->{next_char} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1212            !!!cp (19);            !!!cp (19);
1213            $self->{current_token}            $self->{ct}
1214              = {type => START_TAG_TOKEN,              = {type => START_TAG_TOKEN,
1215                 tag_name => chr ($self->{next_char} + 0x0020),                 tag_name => chr ($self->{nc} + 0x0020),
1216                 line => $self->{line_prev},                 line => $self->{line_prev},
1217                 column => $self->{column_prev}};                 column => $self->{column_prev}};
1218            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1219            !!!next-input-character;            !!!next-input-character;
1220            redo A;            redo A;
1221          } elsif (0x0061 <= $self->{next_char} and          } elsif (0x0061 <= $self->{nc} and
1222                   $self->{next_char} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1223            !!!cp (20);            !!!cp (20);
1224            $self->{current_token} = {type => START_TAG_TOKEN,            $self->{ct} = {type => START_TAG_TOKEN,
1225                                      tag_name => chr ($self->{next_char}),                                      tag_name => chr ($self->{nc}),
1226                                      line => $self->{line_prev},                                      line => $self->{line_prev},
1227                                      column => $self->{column_prev}};                                      column => $self->{column_prev}};
1228            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1229            !!!next-input-character;            !!!next-input-character;
1230            redo A;            redo A;
1231          } elsif ($self->{next_char} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1232            !!!cp (21);            !!!cp (21);
1233            !!!parse-error (type => 'empty start tag',            !!!parse-error (type => 'empty start tag',
1234                            line => $self->{line_prev},                            line => $self->{line_prev},
# Line 1087  sub _get_next_token ($) { Line 1242  sub _get_next_token ($) {
1242                     });                     });
1243    
1244            redo A;            redo A;
1245          } elsif ($self->{next_char} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1246            !!!cp (22);            !!!cp (22);
1247            !!!parse-error (type => 'pio',            !!!parse-error (type => 'pio',
1248                            line => $self->{line_prev},                            line => $self->{line_prev},
1249                            column => $self->{column_prev});                            column => $self->{column_prev});
1250            $self->{state} = BOGUS_COMMENT_STATE;            $self->{state} = BOGUS_COMMENT_STATE;
1251            $self->{current_token} = {type => COMMENT_TOKEN, data => '',            $self->{ct} = {type => COMMENT_TOKEN, data => '',
1252                                      line => $self->{line_prev},                                      line => $self->{line_prev},
1253                                      column => $self->{column_prev},                                      column => $self->{column_prev},
1254                                     };                                     };
1255            ## $self->{next_char} is intentionally left as is            ## $self->{nc} is intentionally left as is
1256            redo A;            redo A;
1257          } else {          } else {
1258            !!!cp (23);            !!!cp (23);
# Line 1118  sub _get_next_token ($) { Line 1273  sub _get_next_token ($) {
1273          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
1274        }        }
1275      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1276          ## NOTE: The "close tag open state" in the spec is implemented as
1277          ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
1278    
1279        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1280        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1281          if (defined $self->{last_emitted_start_tag_name}) {          if (defined $self->{last_stag_name}) {
1282              $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1283            ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>            $self->{s_kwd} = '';
1284            my @next_char;            ## Reconsume.
1285            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...  
           }  
1286          } else {          } else {
1287            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1288              ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1289            !!!cp (28);            !!!cp (28);
           # next-input-character is already done  
1290            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1291              ## Reconsume.
1292            !!!emit ({type => CHARACTER_TOKEN, data => '</',            !!!emit ({type => CHARACTER_TOKEN, data => '</',
1293                      line => $l, column => $c,                      line => $l, column => $c,
1294                     });                     });
1295            redo A;            redo A;
1296          }          }
1297        }        }
1298          
1299        if (0x0041 <= $self->{next_char} and        if (0x0041 <= $self->{nc} and
1300            $self->{next_char} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1301          !!!cp (29);          !!!cp (29);
1302          $self->{current_token}          $self->{ct}
1303              = {type => END_TAG_TOKEN,              = {type => END_TAG_TOKEN,
1304                 tag_name => chr ($self->{next_char} + 0x0020),                 tag_name => chr ($self->{nc} + 0x0020),
1305                 line => $l, column => $c};                 line => $l, column => $c};
1306          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1307          !!!next-input-character;          !!!next-input-character;
1308          redo A;          redo A;
1309        } elsif (0x0061 <= $self->{next_char} and        } elsif (0x0061 <= $self->{nc} and
1310                 $self->{next_char} <= 0x007A) { # a..z                 $self->{nc} <= 0x007A) { # a..z
1311          !!!cp (30);          !!!cp (30);
1312          $self->{current_token} = {type => END_TAG_TOKEN,          $self->{ct} = {type => END_TAG_TOKEN,
1313                                    tag_name => chr ($self->{next_char}),                                    tag_name => chr ($self->{nc}),
1314                                    line => $l, column => $c};                                    line => $l, column => $c};
1315          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1316          !!!next-input-character;          !!!next-input-character;
1317          redo A;          redo A;
1318        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1319          !!!cp (31);          !!!cp (31);
1320          !!!parse-error (type => 'empty end tag',          !!!parse-error (type => 'empty end tag',
1321                          line => $self->{line_prev}, ## "<" in "</>"                          line => $self->{line_prev}, ## "<" in "</>"
# Line 1208  sub _get_next_token ($) { Line 1323  sub _get_next_token ($) {
1323          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1324          !!!next-input-character;          !!!next-input-character;
1325          redo A;          redo A;
1326        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1327          !!!cp (32);          !!!cp (32);
1328          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1329          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
# Line 1223  sub _get_next_token ($) { Line 1338  sub _get_next_token ($) {
1338          !!!cp (33);          !!!cp (33);
1339          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1340          $self->{state} = BOGUS_COMMENT_STATE;          $self->{state} = BOGUS_COMMENT_STATE;
1341          $self->{current_token} = {type => COMMENT_TOKEN, data => '',          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1342                                    line => $self->{line_prev}, # "<" of "</"                                    line => $self->{line_prev}, # "<" of "</"
1343                                    column => $self->{column_prev} - 1,                                    column => $self->{column_prev} - 1,
1344                                   };                                   };
1345          ## $self->{next_char} is intentionally left as is          ## NOTE: $self->{nc} is intentionally left as is.
1346          redo A;          ## Although the "anything else" case of the spec not explicitly
1347            ## states that the next input character is to be reconsumed,
1348            ## it will be included to the |data| of the comment token
1349            ## generated from the bogus end tag, as defined in the
1350            ## "bogus comment state" entry.
1351            redo A;
1352          }
1353        } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1354          my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1355          if (length $ch) {
1356            my $CH = $ch;
1357            $ch =~ tr/a-z/A-Z/;
1358            my $nch = chr $self->{nc};
1359            if ($nch eq $ch or $nch eq $CH) {
1360              !!!cp (24);
1361              ## Stay in the state.
1362              $self->{s_kwd} .= $nch;
1363              !!!next-input-character;
1364              redo A;
1365            } else {
1366              !!!cp (25);
1367              $self->{state} = DATA_STATE;
1368              ## Reconsume.
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            }
1376          } else { # after "<{tag-name}"
1377            unless ($is_space->{$self->{nc}} or
1378                    {
1379                     0x003E => 1, # >
1380                     0x002F => 1, # /
1381                     -1 => 1, # EOF
1382                    }->{$self->{nc}}) {
1383              !!!cp (26);
1384              ## Reconsume.
1385              $self->{state} = DATA_STATE;
1386              !!!emit ({type => CHARACTER_TOKEN,
1387                        data => '</' . $self->{s_kwd},
1388                        line => $self->{line_prev},
1389                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1390                       });
1391              redo A;
1392            } else {
1393              !!!cp (27);
1394              $self->{ct}
1395                  = {type => END_TAG_TOKEN,
1396                     tag_name => $self->{last_stag_name},
1397                     line => $self->{line_prev},
1398                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1399              $self->{state} = TAG_NAME_STATE;
1400              ## Reconsume.
1401              redo A;
1402            }
1403        }        }
1404      } elsif ($self->{state} == TAG_NAME_STATE) {      } elsif ($self->{state} == TAG_NAME_STATE) {
1405        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  
1406          !!!cp (34);          !!!cp (34);
1407          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1408          !!!next-input-character;          !!!next-input-character;
1409          redo A;          redo A;
1410        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1411          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1412            !!!cp (35);            !!!cp (35);
1413            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1414          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1415            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1416            #if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1417            #  ## NOTE: This should never be reached.            #  ## NOTE: This should never be reached.
1418            #  !!! cp (36);            #  !!! cp (36);
1419            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 1254  sub _get_next_token ($) { Line 1421  sub _get_next_token ($) {
1421              !!!cp (37);              !!!cp (37);
1422            #}            #}
1423          } else {          } else {
1424            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1425          }          }
1426          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1427          !!!next-input-character;          !!!next-input-character;
1428    
1429          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1430    
1431          redo A;          redo A;
1432        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1433                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1434          !!!cp (38);          !!!cp (38);
1435          $self->{current_token}->{tag_name} .= chr ($self->{next_char} + 0x0020);          $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1436            # start tag or end tag            # start tag or end tag
1437          ## Stay in this state          ## Stay in this state
1438          !!!next-input-character;          !!!next-input-character;
1439          redo A;          redo A;
1440        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1441          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1442          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1443            !!!cp (39);            !!!cp (39);
1444            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1445          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1446            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1447            #if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1448            #  ## NOTE: This state should never be reached.            #  ## NOTE: This state should never be reached.
1449            #  !!! cp (40);            #  !!! cp (40);
1450            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 1285  sub _get_next_token ($) { Line 1452  sub _get_next_token ($) {
1452              !!!cp (41);              !!!cp (41);
1453            #}            #}
1454          } else {          } else {
1455            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1456          }          }
1457          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1458          # reconsume          # reconsume
1459    
1460          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1461    
1462          redo A;          redo A;
1463        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1464          !!!cp (42);          !!!cp (42);
1465          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
1466          !!!next-input-character;          !!!next-input-character;
1467          redo A;          redo A;
1468        } else {        } else {
1469          !!!cp (44);          !!!cp (44);
1470          $self->{current_token}->{tag_name} .= chr $self->{next_char};          $self->{ct}->{tag_name} .= chr $self->{nc};
1471            # start tag or end tag            # start tag or end tag
1472          ## Stay in the state          ## Stay in the state
1473          !!!next-input-character;          !!!next-input-character;
1474          redo A;          redo A;
1475        }        }
1476      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1477        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  
1478          !!!cp (45);          !!!cp (45);
1479          ## Stay in the state          ## Stay in the state
1480          !!!next-input-character;          !!!next-input-character;
1481          redo A;          redo A;
1482        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1483          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1484            !!!cp (46);            !!!cp (46);
1485            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1486          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1487            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1488            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1489              !!!cp (47);              !!!cp (47);
1490              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1491            } else {            } else {
1492              !!!cp (48);              !!!cp (48);
1493            }            }
1494          } else {          } else {
1495            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1496          }          }
1497          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1498          !!!next-input-character;          !!!next-input-character;
1499    
1500          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1501    
1502          redo A;          redo A;
1503        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1504                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1505          !!!cp (49);          !!!cp (49);
1506          $self->{current_attribute}          $self->{ca}
1507              = {name => chr ($self->{next_char} + 0x0020),              = {name => chr ($self->{nc} + 0x0020),
1508                 value => '',                 value => '',
1509                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1510          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1511          !!!next-input-character;          !!!next-input-character;
1512          redo A;          redo A;
1513        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1514          !!!cp (50);          !!!cp (50);
1515          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
1516          !!!next-input-character;          !!!next-input-character;
1517          redo A;          redo A;
1518        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1519          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1520          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1521            !!!cp (52);            !!!cp (52);
1522            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1523          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1524            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1525            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1526              !!!cp (53);              !!!cp (53);
1527              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1528            } else {            } else {
1529              !!!cp (54);              !!!cp (54);
1530            }            }
1531          } else {          } else {
1532            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1533          }          }
1534          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1535          # reconsume          # reconsume
1536    
1537          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1538    
1539          redo A;          redo A;
1540        } else {        } else {
# Line 1379  sub _get_next_token ($) { Line 1542  sub _get_next_token ($) {
1542               0x0022 => 1, # "               0x0022 => 1, # "
1543               0x0027 => 1, # '               0x0027 => 1, # '
1544               0x003D => 1, # =               0x003D => 1, # =
1545              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1546            !!!cp (55);            !!!cp (55);
1547            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1548          } else {          } else {
1549            !!!cp (56);            !!!cp (56);
1550          }          }
1551          $self->{current_attribute}          $self->{ca}
1552              = {name => chr ($self->{next_char}),              = {name => chr ($self->{nc}),
1553                 value => '',                 value => '',
1554                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1555          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
# Line 1395  sub _get_next_token ($) { Line 1558  sub _get_next_token ($) {
1558        }        }
1559      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1560        my $before_leave = sub {        my $before_leave = sub {
1561          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1562              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1563            !!!cp (57);            !!!cp (57);
1564            !!!parse-error (type => 'duplicate attribute', text => $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});
1565            ## Discard $self->{current_attribute} # MUST            ## Discard $self->{ca} # MUST
1566          } else {          } else {
1567            !!!cp (58);            !!!cp (58);
1568            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            $self->{ct}->{attributes}->{$self->{ca}->{name}}
1569              = $self->{current_attribute};              = $self->{ca};
1570          }          }
1571        }; # $before_leave        }; # $before_leave
1572    
1573        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  
1574          !!!cp (59);          !!!cp (59);
1575          $before_leave->();          $before_leave->();
1576          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1577          !!!next-input-character;          !!!next-input-character;
1578          redo A;          redo A;
1579        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1580          !!!cp (60);          !!!cp (60);
1581          $before_leave->();          $before_leave->();
1582          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1583          !!!next-input-character;          !!!next-input-character;
1584          redo A;          redo A;
1585        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1586          $before_leave->();          $before_leave->();
1587          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1588            !!!cp (61);            !!!cp (61);
1589            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1590          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1591            !!!cp (62);            !!!cp (62);
1592            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1593            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1594              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1595            }            }
1596          } else {          } else {
1597            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1598          }          }
1599          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1600          !!!next-input-character;          !!!next-input-character;
1601    
1602          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1603    
1604          redo A;          redo A;
1605        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1606                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1607          !!!cp (63);          !!!cp (63);
1608          $self->{current_attribute}->{name} .= chr ($self->{next_char} + 0x0020);          $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1609          ## Stay in the state          ## Stay in the state
1610          !!!next-input-character;          !!!next-input-character;
1611          redo A;          redo A;
1612        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1613          !!!cp (64);          !!!cp (64);
1614          $before_leave->();          $before_leave->();
1615          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
1616          !!!next-input-character;          !!!next-input-character;
1617          redo A;          redo A;
1618        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1619          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1620          $before_leave->();          $before_leave->();
1621          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1622            !!!cp (66);            !!!cp (66);
1623            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1624          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1625            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1626            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1627              !!!cp (67);              !!!cp (67);
1628              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1629            } else {            } else {
# Line 1472  sub _get_next_token ($) { Line 1631  sub _get_next_token ($) {
1631              !!!cp (68);              !!!cp (68);
1632            }            }
1633          } else {          } else {
1634            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1635          }          }
1636          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1637          # reconsume          # reconsume
1638    
1639          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1640    
1641          redo A;          redo A;
1642        } else {        } else {
1643          if ($self->{next_char} == 0x0022 or # "          if ($self->{nc} == 0x0022 or # "
1644              $self->{next_char} == 0x0027) { # '              $self->{nc} == 0x0027) { # '
1645            !!!cp (69);            !!!cp (69);
1646            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1647          } else {          } else {
1648            !!!cp (70);            !!!cp (70);
1649          }          }
1650          $self->{current_attribute}->{name} .= chr ($self->{next_char});          $self->{ca}->{name} .= chr ($self->{nc});
1651          ## Stay in the state          ## Stay in the state
1652          !!!next-input-character;          !!!next-input-character;
1653          redo A;          redo A;
1654        }        }
1655      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1656        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  
1657          !!!cp (71);          !!!cp (71);
1658          ## Stay in the state          ## Stay in the state
1659          !!!next-input-character;          !!!next-input-character;
1660          redo A;          redo A;
1661        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1662          !!!cp (72);          !!!cp (72);
1663          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1664          !!!next-input-character;          !!!next-input-character;
1665          redo A;          redo A;
1666        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1667          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1668            !!!cp (73);            !!!cp (73);
1669            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1670          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1671            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1672            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1673              !!!cp (74);              !!!cp (74);
1674              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1675            } else {            } else {
# Line 1522  sub _get_next_token ($) { Line 1677  sub _get_next_token ($) {
1677              !!!cp (75);              !!!cp (75);
1678            }            }
1679          } else {          } else {
1680            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1681          }          }
1682          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1683          !!!next-input-character;          !!!next-input-character;
1684    
1685          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1686    
1687          redo A;          redo A;
1688        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1689                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1690          !!!cp (76);          !!!cp (76);
1691          $self->{current_attribute}          $self->{ca}
1692              = {name => chr ($self->{next_char} + 0x0020),              = {name => chr ($self->{nc} + 0x0020),
1693                 value => '',                 value => '',
1694                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1695          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1696          !!!next-input-character;          !!!next-input-character;
1697          redo A;          redo A;
1698        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1699          !!!cp (77);          !!!cp (77);
1700          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
1701          !!!next-input-character;          !!!next-input-character;
1702          redo A;          redo A;
1703        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1704          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1705          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1706            !!!cp (79);            !!!cp (79);
1707            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1708          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1709            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1710            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1711              !!!cp (80);              !!!cp (80);
1712              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1713            } else {            } else {
# Line 1560  sub _get_next_token ($) { Line 1715  sub _get_next_token ($) {
1715              !!!cp (81);              !!!cp (81);
1716            }            }
1717          } else {          } else {
1718            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1719          }          }
1720          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1721          # reconsume          # reconsume
1722    
1723          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1724    
1725          redo A;          redo A;
1726        } else {        } else {
1727          if ($self->{next_char} == 0x0022 or # "          if ($self->{nc} == 0x0022 or # "
1728              $self->{next_char} == 0x0027) { # '              $self->{nc} == 0x0027) { # '
1729            !!!cp (78);            !!!cp (78);
1730            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1731          } else {          } else {
1732            !!!cp (82);            !!!cp (82);
1733          }          }
1734          $self->{current_attribute}          $self->{ca}
1735              = {name => chr ($self->{next_char}),              = {name => chr ($self->{nc}),
1736                 value => '',                 value => '',
1737                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1738          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
# Line 1585  sub _get_next_token ($) { Line 1740  sub _get_next_token ($) {
1740          redo A;                  redo A;        
1741        }        }
1742      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1743        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        
1744          !!!cp (83);          !!!cp (83);
1745          ## Stay in the state          ## Stay in the state
1746          !!!next-input-character;          !!!next-input-character;
1747          redo A;          redo A;
1748        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1749          !!!cp (84);          !!!cp (84);
1750          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1751          !!!next-input-character;          !!!next-input-character;
1752          redo A;          redo A;
1753        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1754          !!!cp (85);          !!!cp (85);
1755          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1756          ## reconsume          ## reconsume
1757          redo A;          redo A;
1758        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1759          !!!cp (86);          !!!cp (86);
1760          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1761          !!!next-input-character;          !!!next-input-character;
1762          redo A;          redo A;
1763        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1764          !!!parse-error (type => 'empty unquoted attribute value');          !!!parse-error (type => 'empty unquoted attribute value');
1765          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1766            !!!cp (87);            !!!cp (87);
1767            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1768          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1769            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1770            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1771              !!!cp (88);              !!!cp (88);
1772              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1773            } else {            } else {
# Line 1624  sub _get_next_token ($) { Line 1775  sub _get_next_token ($) {
1775              !!!cp (89);              !!!cp (89);
1776            }            }
1777          } else {          } else {
1778            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1779          }          }
1780          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1781          !!!next-input-character;          !!!next-input-character;
1782    
1783          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1784    
1785          redo A;          redo A;
1786        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1787          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1788          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1789            !!!cp (90);            !!!cp (90);
1790            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1791          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1792            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1793            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1794              !!!cp (91);              !!!cp (91);
1795              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1796            } else {            } else {
# Line 1647  sub _get_next_token ($) { Line 1798  sub _get_next_token ($) {
1798              !!!cp (92);              !!!cp (92);
1799            }            }
1800          } else {          } else {
1801            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1802          }          }
1803          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1804          ## reconsume          ## reconsume
1805    
1806          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1807    
1808          redo A;          redo A;
1809        } else {        } else {
1810          if ($self->{next_char} == 0x003D) { # =          if ($self->{nc} == 0x003D) { # =
1811            !!!cp (93);            !!!cp (93);
1812            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1813          } else {          } else {
1814            !!!cp (94);            !!!cp (94);
1815          }          }
1816          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1817          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1818          !!!next-input-character;          !!!next-input-character;
1819          redo A;          redo A;
1820        }        }
1821      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1822        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1823          !!!cp (95);          !!!cp (95);
1824          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1825          !!!next-input-character;          !!!next-input-character;
1826          redo A;          redo A;
1827        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1828          !!!cp (96);          !!!cp (96);
1829          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1830          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1831            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1832            ## implementation of the "consume a character reference" algorithm.
1833            $self->{prev_state} = $self->{state};
1834            $self->{entity_add} = 0x0022; # "
1835            $self->{state} = ENTITY_STATE;
1836          !!!next-input-character;          !!!next-input-character;
1837          redo A;          redo A;
1838        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1839          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1840          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1841            !!!cp (97);            !!!cp (97);
1842            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1843          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1844            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1845            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1846              !!!cp (98);              !!!cp (98);
1847              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1848            } else {            } else {
# Line 1694  sub _get_next_token ($) { Line 1850  sub _get_next_token ($) {
1850              !!!cp (99);              !!!cp (99);
1851            }            }
1852          } else {          } else {
1853            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1854          }          }
1855          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1856          ## reconsume          ## reconsume
1857    
1858          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1859    
1860          redo A;          redo A;
1861        } else {        } else {
1862          !!!cp (100);          !!!cp (100);
1863          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1864            $self->{read_until}->($self->{ca}->{value},
1865                                  q["&],
1866                                  length $self->{ca}->{value});
1867    
1868          ## Stay in the state          ## Stay in the state
1869          !!!next-input-character;          !!!next-input-character;
1870          redo A;          redo A;
1871        }        }
1872      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1873        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1874          !!!cp (101);          !!!cp (101);
1875          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1876          !!!next-input-character;          !!!next-input-character;
1877          redo A;          redo A;
1878        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1879          !!!cp (102);          !!!cp (102);
1880          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1881          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1882            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1883            ## implementation of the "consume a character reference" algorithm.
1884            $self->{entity_add} = 0x0027; # '
1885            $self->{prev_state} = $self->{state};
1886            $self->{state} = ENTITY_STATE;
1887          !!!next-input-character;          !!!next-input-character;
1888          redo A;          redo A;
1889        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1890          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1891          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1892            !!!cp (103);            !!!cp (103);
1893            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1894          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1895            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1896            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1897              !!!cp (104);              !!!cp (104);
1898              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1899            } else {            } else {
# Line 1736  sub _get_next_token ($) { Line 1901  sub _get_next_token ($) {
1901              !!!cp (105);              !!!cp (105);
1902            }            }
1903          } else {          } else {
1904            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1905          }          }
1906          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1907          ## reconsume          ## reconsume
1908    
1909          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1910    
1911          redo A;          redo A;
1912        } else {        } else {
1913          !!!cp (106);          !!!cp (106);
1914          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1915            $self->{read_until}->($self->{ca}->{value},
1916                                  q['&],
1917                                  length $self->{ca}->{value});
1918    
1919          ## Stay in the state          ## Stay in the state
1920          !!!next-input-character;          !!!next-input-character;
1921          redo A;          redo A;
1922        }        }
1923      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1924        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  
1925          !!!cp (107);          !!!cp (107);
1926          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1927          !!!next-input-character;          !!!next-input-character;
1928          redo A;          redo A;
1929        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1930          !!!cp (108);          !!!cp (108);
1931          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1932          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1933            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1934            ## implementation of the "consume a character reference" algorithm.
1935            $self->{entity_add} = -1;
1936            $self->{prev_state} = $self->{state};
1937            $self->{state} = ENTITY_STATE;
1938          !!!next-input-character;          !!!next-input-character;
1939          redo A;          redo A;
1940        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1941          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1942            !!!cp (109);            !!!cp (109);
1943            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1944          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1945            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1946            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1947              !!!cp (110);              !!!cp (110);
1948              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1949            } else {            } else {
# Line 1781  sub _get_next_token ($) { Line 1951  sub _get_next_token ($) {
1951              !!!cp (111);              !!!cp (111);
1952            }            }
1953          } else {          } else {
1954            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1955          }          }
1956          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1957          !!!next-input-character;          !!!next-input-character;
1958    
1959          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1960    
1961          redo A;          redo A;
1962        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1963          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1964          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1965            !!!cp (112);            !!!cp (112);
1966            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1967          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1968            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1969            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1970              !!!cp (113);              !!!cp (113);
1971              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1972            } else {            } else {
# Line 1804  sub _get_next_token ($) { Line 1974  sub _get_next_token ($) {
1974              !!!cp (114);              !!!cp (114);
1975            }            }
1976          } else {          } else {
1977            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1978          }          }
1979          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1980          ## reconsume          ## reconsume
1981    
1982          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1983    
1984          redo A;          redo A;
1985        } else {        } else {
# Line 1817  sub _get_next_token ($) { Line 1987  sub _get_next_token ($) {
1987               0x0022 => 1, # "               0x0022 => 1, # "
1988               0x0027 => 1, # '               0x0027 => 1, # '
1989               0x003D => 1, # =               0x003D => 1, # =
1990              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1991            !!!cp (115);            !!!cp (115);
1992            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1993          } else {          } else {
1994            !!!cp (116);            !!!cp (116);
1995          }          }
1996          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1997            $self->{read_until}->($self->{ca}->{value},
1998                                  q["'=& >],
1999                                  length $self->{ca}->{value});
2000    
2001          ## Stay in the state          ## Stay in the state
2002          !!!next-input-character;          !!!next-input-character;
2003          redo A;          redo A;
2004        }        }
     } 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;  
2005      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2006        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  
2007          !!!cp (118);          !!!cp (118);
2008          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2009          !!!next-input-character;          !!!next-input-character;
2010          redo A;          redo A;
2011        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2012          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2013            !!!cp (119);            !!!cp (119);
2014            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
2015          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2016            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2017            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2018              !!!cp (120);              !!!cp (120);
2019              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2020            } else {            } else {
# Line 1874  sub _get_next_token ($) { Line 2022  sub _get_next_token ($) {
2022              !!!cp (121);              !!!cp (121);
2023            }            }
2024          } else {          } else {
2025            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
2026          }          }
2027          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2028          !!!next-input-character;          !!!next-input-character;
2029    
2030          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2031    
2032          redo A;          redo A;
2033        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
2034          !!!cp (122);          !!!cp (122);
2035          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
2036          !!!next-input-character;          !!!next-input-character;
2037          redo A;          redo A;
2038        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2039          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
2040          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2041            !!!cp (122.3);            !!!cp (122.3);
2042            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
2043          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2044            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2045              !!!cp (122.1);              !!!cp (122.1);
2046              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2047            } else {            } else {
# Line 1901  sub _get_next_token ($) { Line 2049  sub _get_next_token ($) {
2049              !!!cp (122.2);              !!!cp (122.2);
2050            }            }
2051          } else {          } else {
2052            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
2053          }          }
2054          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2055          ## Reconsume.          ## Reconsume.
2056          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2057          redo A;          redo A;
2058        } else {        } else {
2059          !!!cp ('124.1');          !!!cp ('124.1');
# Line 1915  sub _get_next_token ($) { Line 2063  sub _get_next_token ($) {
2063          redo A;          redo A;
2064        }        }
2065      } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {      } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2066        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2067          if ($self->{current_token}->{type} == END_TAG_TOKEN) {          if ($self->{ct}->{type} == END_TAG_TOKEN) {
2068            !!!cp ('124.2');            !!!cp ('124.2');
2069            !!!parse-error (type => 'nestc', token => $self->{current_token});            !!!parse-error (type => 'nestc', token => $self->{ct});
2070            ## TODO: Different type than slash in start tag            ## TODO: Different type than slash in start tag
2071            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2072            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2073              !!!cp ('124.4');              !!!cp ('124.4');
2074              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2075            } else {            } else {
# Line 1936  sub _get_next_token ($) { Line 2084  sub _get_next_token ($) {
2084          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2085          !!!next-input-character;          !!!next-input-character;
2086    
2087          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2088    
2089          redo A;          redo A;
2090        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2091          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
2092          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2093            !!!cp (124.7);            !!!cp (124.7);
2094            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
2095          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2096            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2097              !!!cp (124.5);              !!!cp (124.5);
2098              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2099            } else {            } else {
# Line 1953  sub _get_next_token ($) { Line 2101  sub _get_next_token ($) {
2101              !!!cp (124.6);              !!!cp (124.6);
2102            }            }
2103          } else {          } else {
2104            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
2105          }          }
2106          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2107          ## Reconsume.          ## Reconsume.
2108          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2109          redo A;          redo A;
2110        } else {        } else {
2111          !!!cp ('124.4');          !!!cp ('124.4');
# Line 1969  sub _get_next_token ($) { Line 2117  sub _get_next_token ($) {
2117        }        }
2118      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2119        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
         
       ## NOTE: Set by the previous state  
       #my $token = {type => COMMENT_TOKEN, data => ''};  
2120    
2121        BC: {        ## NOTE: Unlike spec's "bogus comment state", this implementation
2122          if ($self->{next_char} == 0x003E) { # >        ## consumes characters one-by-one basis.
2123            !!!cp (124);        
2124            $self->{state} = DATA_STATE;        if ($self->{nc} == 0x003E) { # >
2125            !!!next-input-character;          !!!cp (124);
2126            $self->{state} = DATA_STATE;
2127            !!!emit ($self->{current_token}); # comment          !!!next-input-character;
   
           redo A;  
         } elsif ($self->{next_char} == -1) {  
           !!!cp (125);  
           $self->{state} = DATA_STATE;  
           ## reconsume  
2128    
2129            !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2130            redo A;
2131          } elsif ($self->{nc} == -1) {
2132            !!!cp (125);
2133            $self->{state} = DATA_STATE;
2134            ## reconsume
2135    
2136            redo A;          !!!emit ($self->{ct}); # comment
2137          } else {          redo A;
2138            !!!cp (126);        } else {
2139            $self->{current_token}->{data} .= chr ($self->{next_char}); # comment          !!!cp (126);
2140            !!!next-input-character;          $self->{ct}->{data} .= chr ($self->{nc}); # comment
2141            redo BC;          $self->{read_until}->($self->{ct}->{data},
2142          }                                q[>],
2143        } # BC                                length $self->{ct}->{data});
2144    
2145        die "$0: _get_next_token: unexpected case [BC]";          ## Stay in the state.
2146            !!!next-input-character;
2147            redo A;
2148          }
2149      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2150        ## (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};  
2151                
2152        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2153            !!!cp (133);
2154            $self->{state} = MD_HYPHEN_STATE;
2155          !!!next-input-character;          !!!next-input-character;
2156          push @next_char, $self->{next_char};          redo A;
2157          if ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x0044 or # D
2158            !!!cp (127);                 $self->{nc} == 0x0064) { # d
2159            $self->{current_token} = {type => COMMENT_TOKEN, data => '',          ## ASCII case-insensitive.
2160                                      line => $l, column => $c,          !!!cp (130);
2161                                     };          $self->{state} = MD_DOCTYPE_STATE;
2162            $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  
2163          !!!next-input-character;          !!!next-input-character;
2164          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);  
         }  
2165        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2166                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2167                 $self->{next_char} == 0x005B) { # [                 $self->{nc} == 0x005B) { # [
2168            !!!cp (135.4);                
2169            $self->{state} = MD_CDATA_STATE;
2170            $self->{s_kwd} = '[';
2171          !!!next-input-character;          !!!next-input-character;
2172          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);  
         }  
2173        } else {        } else {
2174          !!!cp (136);          !!!cp (136);
2175        }        }
2176    
2177        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment',
2178        $self->{next_char} = shift @next_char;                        line => $self->{line_prev},
2179        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2180          ## Reconsume.
2181        $self->{state} = BOGUS_COMMENT_STATE;        $self->{state} = BOGUS_COMMENT_STATE;
2182        $self->{current_token} = {type => COMMENT_TOKEN, data => '',        $self->{ct} = {type => COMMENT_TOKEN, data => '',
2183                                  line => $l, column => $c,                                  line => $self->{line_prev},
2184                                    column => $self->{column_prev} - 1,
2185                                 };                                 };
2186        redo A;        redo A;
2187              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2188        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2189        ## 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);
2190            $self->{ct} = {type => COMMENT_TOKEN, data => '',
2191                                      line => $self->{line_prev},
2192                                      column => $self->{column_prev} - 2,
2193                                     };
2194            $self->{state} = COMMENT_START_STATE;
2195            !!!next-input-character;
2196            redo A;
2197          } else {
2198            !!!cp (128);
2199            !!!parse-error (type => 'bogus comment',
2200                            line => $self->{line_prev},
2201                            column => $self->{column_prev} - 2);
2202            $self->{state} = BOGUS_COMMENT_STATE;
2203            ## Reconsume.
2204            $self->{ct} = {type => COMMENT_TOKEN,
2205                                      data => '-',
2206                                      line => $self->{line_prev},
2207                                      column => $self->{column_prev} - 2,
2208                                     };
2209            redo A;
2210          }
2211        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2212          ## ASCII case-insensitive.
2213          if ($self->{nc} == [
2214                undef,
2215                0x004F, # O
2216                0x0043, # C
2217                0x0054, # T
2218                0x0059, # Y
2219                0x0050, # P
2220              ]->[length $self->{s_kwd}] or
2221              $self->{nc} == [
2222                undef,
2223                0x006F, # o
2224                0x0063, # c
2225                0x0074, # t
2226                0x0079, # y
2227                0x0070, # p
2228              ]->[length $self->{s_kwd}]) {
2229            !!!cp (131);
2230            ## Stay in the state.
2231            $self->{s_kwd} .= chr $self->{nc};
2232            !!!next-input-character;
2233            redo A;
2234          } elsif ((length $self->{s_kwd}) == 6 and
2235                   ($self->{nc} == 0x0045 or # E
2236                    $self->{nc} == 0x0065)) { # e
2237            !!!cp (129);
2238            $self->{state} = DOCTYPE_STATE;
2239            $self->{ct} = {type => DOCTYPE_TOKEN,
2240                                      quirks => 1,
2241                                      line => $self->{line_prev},
2242                                      column => $self->{column_prev} - 7,
2243                                     };
2244            !!!next-input-character;
2245            redo A;
2246          } else {
2247            !!!cp (132);        
2248            !!!parse-error (type => 'bogus comment',
2249                            line => $self->{line_prev},
2250                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2251            $self->{state} = BOGUS_COMMENT_STATE;
2252            ## Reconsume.
2253            $self->{ct} = {type => COMMENT_TOKEN,
2254                                      data => $self->{s_kwd},
2255                                      line => $self->{line_prev},
2256                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2257                                     };
2258            redo A;
2259          }
2260        } elsif ($self->{state} == MD_CDATA_STATE) {
2261          if ($self->{nc} == {
2262                '[' => 0x0043, # C
2263                '[C' => 0x0044, # D
2264                '[CD' => 0x0041, # A
2265                '[CDA' => 0x0054, # T
2266                '[CDAT' => 0x0041, # A
2267              }->{$self->{s_kwd}}) {
2268            !!!cp (135.1);
2269            ## Stay in the state.
2270            $self->{s_kwd} .= chr $self->{nc};
2271            !!!next-input-character;
2272            redo A;
2273          } elsif ($self->{s_kwd} eq '[CDATA' and
2274                   $self->{nc} == 0x005B) { # [
2275            !!!cp (135.2);
2276            $self->{ct} = {type => CHARACTER_TOKEN,
2277                                      data => '',
2278                                      line => $self->{line_prev},
2279                                      column => $self->{column_prev} - 7};
2280            $self->{state} = CDATA_SECTION_STATE;
2281            !!!next-input-character;
2282            redo A;
2283          } else {
2284            !!!cp (135.3);
2285            !!!parse-error (type => 'bogus comment',
2286                            line => $self->{line_prev},
2287                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2288            $self->{state} = BOGUS_COMMENT_STATE;
2289            ## Reconsume.
2290            $self->{ct} = {type => COMMENT_TOKEN,
2291                                      data => $self->{s_kwd},
2292                                      line => $self->{line_prev},
2293                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2294                                     };
2295            redo A;
2296          }
2297      } elsif ($self->{state} == COMMENT_START_STATE) {      } elsif ($self->{state} == COMMENT_START_STATE) {
2298        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2299          !!!cp (137);          !!!cp (137);
2300          $self->{state} = COMMENT_START_DASH_STATE;          $self->{state} = COMMENT_START_DASH_STATE;
2301          !!!next-input-character;          !!!next-input-character;
2302          redo A;          redo A;
2303        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2304          !!!cp (138);          !!!cp (138);
2305          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2306          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2307          !!!next-input-character;          !!!next-input-character;
2308    
2309          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2310    
2311          redo A;          redo A;
2312        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2313          !!!cp (139);          !!!cp (139);
2314          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2315          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2316          ## reconsume          ## reconsume
2317    
2318          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2319    
2320          redo A;          redo A;
2321        } else {        } else {
2322          !!!cp (140);          !!!cp (140);
2323          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2324              .= chr ($self->{next_char});              .= chr ($self->{nc});
2325          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2326          !!!next-input-character;          !!!next-input-character;
2327          redo A;          redo A;
2328        }        }
2329      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2330        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2331          !!!cp (141);          !!!cp (141);
2332          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2333          !!!next-input-character;          !!!next-input-character;
2334          redo A;          redo A;
2335        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2336          !!!cp (142);          !!!cp (142);
2337          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2338          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2339          !!!next-input-character;          !!!next-input-character;
2340    
2341          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2342    
2343          redo A;          redo A;
2344        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2345          !!!cp (143);          !!!cp (143);
2346          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2347          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2348          ## reconsume          ## reconsume
2349    
2350          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2351    
2352          redo A;          redo A;
2353        } else {        } else {
2354          !!!cp (144);          !!!cp (144);
2355          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2356              .= '-' . chr ($self->{next_char});              .= '-' . chr ($self->{nc});
2357          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2358          !!!next-input-character;          !!!next-input-character;
2359          redo A;          redo A;
2360        }        }
2361      } elsif ($self->{state} == COMMENT_STATE) {      } elsif ($self->{state} == COMMENT_STATE) {
2362        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2363          !!!cp (145);          !!!cp (145);
2364          $self->{state} = COMMENT_END_DASH_STATE;          $self->{state} = COMMENT_END_DASH_STATE;
2365          !!!next-input-character;          !!!next-input-character;
2366          redo A;          redo A;
2367        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2368          !!!cp (146);          !!!cp (146);
2369          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2370          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2371          ## reconsume          ## reconsume
2372    
2373          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2374    
2375          redo A;          redo A;
2376        } else {        } else {
2377          !!!cp (147);          !!!cp (147);
2378          $self->{current_token}->{data} .= chr ($self->{next_char}); # comment          $self->{ct}->{data} .= chr ($self->{nc}); # comment
2379            $self->{read_until}->($self->{ct}->{data},
2380                                  q[-],
2381                                  length $self->{ct}->{data});
2382    
2383          ## Stay in the state          ## Stay in the state
2384          !!!next-input-character;          !!!next-input-character;
2385          redo A;          redo A;
2386        }        }
2387      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2388        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2389          !!!cp (148);          !!!cp (148);
2390          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2391          !!!next-input-character;          !!!next-input-character;
2392          redo A;          redo A;
2393        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2394          !!!cp (149);          !!!cp (149);
2395          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2396          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2397          ## reconsume          ## reconsume
2398    
2399          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2400    
2401          redo A;          redo A;
2402        } else {        } else {
2403          !!!cp (150);          !!!cp (150);
2404          $self->{current_token}->{data} .= '-' . chr ($self->{next_char}); # comment          $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2405          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2406          !!!next-input-character;          !!!next-input-character;
2407          redo A;          redo A;
2408        }        }
2409      } elsif ($self->{state} == COMMENT_END_STATE) {      } elsif ($self->{state} == COMMENT_END_STATE) {
2410        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2411          !!!cp (151);          !!!cp (151);
2412          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2413          !!!next-input-character;          !!!next-input-character;
2414    
2415          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2416    
2417          redo A;          redo A;
2418        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2419          !!!cp (152);          !!!cp (152);
2420          !!!parse-error (type => 'dash in comment',          !!!parse-error (type => 'dash in comment',
2421                          line => $self->{line_prev},                          line => $self->{line_prev},
2422                          column => $self->{column_prev});                          column => $self->{column_prev});
2423          $self->{current_token}->{data} .= '-'; # comment          $self->{ct}->{data} .= '-'; # comment
2424          ## Stay in the state          ## Stay in the state
2425          !!!next-input-character;          !!!next-input-character;
2426          redo A;          redo A;
2427        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2428          !!!cp (153);          !!!cp (153);
2429          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2430          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2431          ## reconsume          ## reconsume
2432    
2433          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2434    
2435          redo A;          redo A;
2436        } else {        } else {
# Line 2272  sub _get_next_token ($) { Line 2438  sub _get_next_token ($) {
2438          !!!parse-error (type => 'dash in comment',          !!!parse-error (type => 'dash in comment',
2439                          line => $self->{line_prev},                          line => $self->{line_prev},
2440                          column => $self->{column_prev});                          column => $self->{column_prev});
2441          $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment          $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2442          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2443          !!!next-input-character;          !!!next-input-character;
2444          redo A;          redo A;
2445        }        }
2446      } elsif ($self->{state} == DOCTYPE_STATE) {      } elsif ($self->{state} == DOCTYPE_STATE) {
2447        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  
2448          !!!cp (155);          !!!cp (155);
2449          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2450          !!!next-input-character;          !!!next-input-character;
# Line 2295  sub _get_next_token ($) { Line 2457  sub _get_next_token ($) {
2457          redo A;          redo A;
2458        }        }
2459      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2460        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  
2461          !!!cp (157);          !!!cp (157);
2462          ## Stay in the state          ## Stay in the state
2463          !!!next-input-character;          !!!next-input-character;
2464          redo A;          redo A;
2465        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2466          !!!cp (158);          !!!cp (158);
2467          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2468          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2469          !!!next-input-character;          !!!next-input-character;
2470    
2471          !!!emit ($self->{current_token}); # DOCTYPE (quirks)          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2472    
2473          redo A;          redo A;
2474        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2475          !!!cp (159);          !!!cp (159);
2476          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2477          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2478          ## reconsume          ## reconsume
2479    
2480          !!!emit ($self->{current_token}); # DOCTYPE (quirks)          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2481    
2482          redo A;          redo A;
2483        } else {        } else {
2484          !!!cp (160);          !!!cp (160);
2485          $self->{current_token}->{name} = chr $self->{next_char};          $self->{ct}->{name} = chr $self->{nc};
2486          delete $self->{current_token}->{quirks};          delete $self->{ct}->{quirks};
 ## ISSUE: "Set the token's name name to the" in the spec  
2487          $self->{state} = DOCTYPE_NAME_STATE;          $self->{state} = DOCTYPE_NAME_STATE;
2488          !!!next-input-character;          !!!next-input-character;
2489          redo A;          redo A;
2490        }        }
2491      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2492  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2493        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2494          !!!cp (161);          !!!cp (161);
2495          $self->{state} = AFTER_DOCTYPE_NAME_STATE;          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
2496          !!!next-input-character;          !!!next-input-character;
2497          redo A;          redo A;
2498        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2499          !!!cp (162);          !!!cp (162);
2500          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2501          !!!next-input-character;          !!!next-input-character;
2502    
2503          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2504    
2505          redo A;          redo A;
2506        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2507          !!!cp (163);          !!!cp (163);
2508          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2509          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2510          ## reconsume          ## reconsume
2511    
2512          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2513          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2514    
2515          redo A;          redo A;
2516        } else {        } else {
2517          !!!cp (164);          !!!cp (164);
2518          $self->{current_token}->{name}          $self->{ct}->{name}
2519            .= chr ($self->{next_char}); # DOCTYPE            .= chr ($self->{nc}); # DOCTYPE
2520          ## Stay in the state          ## Stay in the state
2521          !!!next-input-character;          !!!next-input-character;
2522          redo A;          redo A;
2523        }        }
2524      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2525        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2526          !!!cp (165);          !!!cp (165);
2527          ## Stay in the state          ## Stay in the state
2528          !!!next-input-character;          !!!next-input-character;
2529          redo A;          redo A;
2530        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2531          !!!cp (166);          !!!cp (166);
2532          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2533          !!!next-input-character;          !!!next-input-character;
2534    
2535          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2536    
2537          redo A;          redo A;
2538        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2539          !!!cp (167);          !!!cp (167);
2540          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2541          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2542          ## reconsume          ## reconsume
2543    
2544          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2545          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2546    
2547          redo A;          redo A;
2548        } elsif ($self->{next_char} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2549                 $self->{next_char} == 0x0070) { # p                 $self->{nc} == 0x0070) { # p
2550            $self->{state} = PUBLIC_STATE;
2551            $self->{s_kwd} = chr $self->{nc};
2552          !!!next-input-character;          !!!next-input-character;
2553          if ($self->{next_char} == 0x0055 or # U          redo A;
2554              $self->{next_char} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2555            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2556            if ($self->{next_char} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2557                $self->{next_char} == 0x0062) { # b          $self->{s_kwd} = chr $self->{nc};
             !!!next-input-character;  
             if ($self->{next_char} == 0x004C or # L  
                 $self->{next_char} == 0x006C) { # l  
               !!!next-input-character;  
               if ($self->{next_char} == 0x0049 or # I  
                   $self->{next_char} == 0x0069) { # i  
                 !!!next-input-character;  
                 if ($self->{next_char} == 0x0043 or # C  
                     $self->{next_char} == 0x0063) { # c  
                   !!!cp (168);  
                   $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
                   !!!next-input-character;  
                   redo A;  
                 } else {  
                   !!!cp (169);  
                 }  
               } else {  
                 !!!cp (170);  
               }  
             } else {  
               !!!cp (171);  
             }  
           } else {  
             !!!cp (172);  
           }  
         } else {  
           !!!cp (173);  
         }  
   
         #  
       } elsif ($self->{next_char} == 0x0053 or # S  
                $self->{next_char} == 0x0073) { # s  
2558          !!!next-input-character;          !!!next-input-character;
2559          if ($self->{next_char} == 0x0059 or # Y          redo A;
             $self->{next_char} == 0x0079) { # y  
           !!!next-input-character;  
           if ($self->{next_char} == 0x0053 or # S  
               $self->{next_char} == 0x0073) { # s  
             !!!next-input-character;  
             if ($self->{next_char} == 0x0054 or # T  
                 $self->{next_char} == 0x0074) { # t  
               !!!next-input-character;  
               if ($self->{next_char} == 0x0045 or # E  
                   $self->{next_char} == 0x0065) { # e  
                 !!!next-input-character;  
                 if ($self->{next_char} == 0x004D or # M  
                     $self->{next_char} == 0x006D) { # m  
                   !!!cp (174);  
                   $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
                   !!!next-input-character;  
                   redo A;  
                 } else {  
                   !!!cp (175);  
                 }  
               } else {  
                 !!!cp (176);  
               }  
             } else {  
               !!!cp (177);  
             }  
           } else {  
             !!!cp (178);  
           }  
         } else {  
           !!!cp (179);  
         }  
   
         #  
2560        } else {        } else {
2561          !!!cp (180);          !!!cp (180);
2562            !!!parse-error (type => 'string after DOCTYPE name');
2563            $self->{ct}->{quirks} = 1;
2564    
2565            $self->{state} = BOGUS_DOCTYPE_STATE;
2566          !!!next-input-character;          !!!next-input-character;
2567          #          redo A;
2568        }        }
2569        } elsif ($self->{state} == PUBLIC_STATE) {
2570          ## ASCII case-insensitive
2571          if ($self->{nc} == [
2572                undef,
2573                0x0055, # U
2574                0x0042, # B
2575                0x004C, # L
2576                0x0049, # I
2577              ]->[length $self->{s_kwd}] or
2578              $self->{nc} == [
2579                undef,
2580                0x0075, # u
2581                0x0062, # b
2582                0x006C, # l
2583                0x0069, # i
2584              ]->[length $self->{s_kwd}]) {
2585            !!!cp (175);
2586            ## Stay in the state.
2587            $self->{s_kwd} .= chr $self->{nc};
2588            !!!next-input-character;
2589            redo A;
2590          } elsif ((length $self->{s_kwd}) == 5 and
2591                   ($self->{nc} == 0x0043 or # C
2592                    $self->{nc} == 0x0063)) { # c
2593            !!!cp (168);
2594            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2595            !!!next-input-character;
2596            redo A;
2597          } else {
2598            !!!cp (169);
2599            !!!parse-error (type => 'string after DOCTYPE name',
2600                            line => $self->{line_prev},
2601                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2602            $self->{ct}->{quirks} = 1;
2603    
2604        !!!parse-error (type => 'string after DOCTYPE name');          $self->{state} = BOGUS_DOCTYPE_STATE;
2605        $self->{current_token}->{quirks} = 1;          ## Reconsume.
2606            redo A;
2607          }
2608        } elsif ($self->{state} == SYSTEM_STATE) {
2609          ## ASCII case-insensitive
2610          if ($self->{nc} == [
2611                undef,
2612                0x0059, # Y
2613                0x0053, # S
2614                0x0054, # T
2615                0x0045, # E
2616              ]->[length $self->{s_kwd}] or
2617              $self->{nc} == [
2618                undef,
2619                0x0079, # y
2620                0x0073, # s
2621                0x0074, # t
2622                0x0065, # e
2623              ]->[length $self->{s_kwd}]) {
2624            !!!cp (170);
2625            ## Stay in the state.
2626            $self->{s_kwd} .= chr $self->{nc};
2627            !!!next-input-character;
2628            redo A;
2629          } elsif ((length $self->{s_kwd}) == 5 and
2630                   ($self->{nc} == 0x004D or # M
2631                    $self->{nc} == 0x006D)) { # m
2632            !!!cp (171);
2633            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2634            !!!next-input-character;
2635            redo A;
2636          } else {
2637            !!!cp (172);
2638            !!!parse-error (type => 'string after DOCTYPE name',
2639                            line => $self->{line_prev},
2640                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2641            $self->{ct}->{quirks} = 1;
2642    
2643        $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2644        # next-input-character is already done          ## Reconsume.
2645        redo A;          redo A;
2646          }
2647      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2648        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2649          !!!cp (181);          !!!cp (181);
2650          ## Stay in the state          ## Stay in the state
2651          !!!next-input-character;          !!!next-input-character;
2652          redo A;          redo A;
2653        } elsif ($self->{next_char} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2654          !!!cp (182);          !!!cp (182);
2655          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2656          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2657          !!!next-input-character;          !!!next-input-character;
2658          redo A;          redo A;
2659        } elsif ($self->{next_char} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2660          !!!cp (183);          !!!cp (183);
2661          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2662          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2663          !!!next-input-character;          !!!next-input-character;
2664          redo A;          redo A;
2665        } elsif ($self->{next_char} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2666          !!!cp (184);          !!!cp (184);
2667          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2668    
2669          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2670          !!!next-input-character;          !!!next-input-character;
2671    
2672          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2673          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2674    
2675          redo A;          redo A;
2676        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2677          !!!cp (185);          !!!cp (185);
2678          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2679    
2680          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2681          ## reconsume          ## reconsume
2682    
2683          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2684          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2685    
2686          redo A;          redo A;
2687        } else {        } else {
2688          !!!cp (186);          !!!cp (186);
2689          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2690          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2691    
2692          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2693          !!!next-input-character;          !!!next-input-character;
2694          redo A;          redo A;
2695        }        }
2696      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2697        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2698          !!!cp (187);          !!!cp (187);
2699          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2700          !!!next-input-character;          !!!next-input-character;
2701          redo A;          redo A;
2702        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2703          !!!cp (188);          !!!cp (188);
2704          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2705    
2706          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2707          !!!next-input-character;          !!!next-input-character;
2708    
2709          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2710          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2711    
2712          redo A;          redo A;
2713        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2714          !!!cp (189);          !!!cp (189);
2715          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2716    
2717          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2718          ## reconsume          ## reconsume
2719    
2720          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2721          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2722    
2723          redo A;          redo A;
2724        } else {        } else {
2725          !!!cp (190);          !!!cp (190);
2726          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2727              .= chr $self->{next_char};              .= chr $self->{nc};
2728            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2729                                  length $self->{ct}->{pubid});
2730    
2731          ## Stay in the state          ## Stay in the state
2732          !!!next-input-character;          !!!next-input-character;
2733          redo A;          redo A;
2734        }        }
2735      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2736        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2737          !!!cp (191);          !!!cp (191);
2738          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2739          !!!next-input-character;          !!!next-input-character;
2740          redo A;          redo A;
2741        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2742          !!!cp (192);          !!!cp (192);
2743          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2744    
2745          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2746          !!!next-input-character;          !!!next-input-character;
2747    
2748          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2749          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2750    
2751          redo A;          redo A;
2752        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2753          !!!cp (193);          !!!cp (193);
2754          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2755    
2756          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2757          ## reconsume          ## reconsume
2758    
2759          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2760          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2761    
2762          redo A;          redo A;
2763        } else {        } else {
2764          !!!cp (194);          !!!cp (194);
2765          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2766              .= chr $self->{next_char};              .= chr $self->{nc};
2767            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2768                                  length $self->{ct}->{pubid});
2769    
2770          ## Stay in the state          ## Stay in the state
2771          !!!next-input-character;          !!!next-input-character;
2772          redo A;          redo A;
2773        }        }
2774      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2775        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2776          !!!cp (195);          !!!cp (195);
2777          ## Stay in the state          ## Stay in the state
2778          !!!next-input-character;          !!!next-input-character;
2779          redo A;          redo A;
2780        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2781          !!!cp (196);          !!!cp (196);
2782          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2783          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2784          !!!next-input-character;          !!!next-input-character;
2785          redo A;          redo A;
2786        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2787          !!!cp (197);          !!!cp (197);
2788          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2789          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2790          !!!next-input-character;          !!!next-input-character;
2791          redo A;          redo A;
2792        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2793          !!!cp (198);          !!!cp (198);
2794          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2795          !!!next-input-character;          !!!next-input-character;
2796    
2797          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2798    
2799          redo A;          redo A;
2800        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2801          !!!cp (199);          !!!cp (199);
2802          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2803    
2804          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2805          ## reconsume          ## reconsume
2806    
2807          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2808          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2809    
2810          redo A;          redo A;
2811        } else {        } else {
2812          !!!cp (200);          !!!cp (200);
2813          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2814          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2815    
2816          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2817          !!!next-input-character;          !!!next-input-character;
2818          redo A;          redo A;
2819        }        }
2820      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2821        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2822          !!!cp (201);          !!!cp (201);
2823          ## Stay in the state          ## Stay in the state
2824          !!!next-input-character;          !!!next-input-character;
2825          redo A;          redo A;
2826        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2827          !!!cp (202);          !!!cp (202);
2828          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2829          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2830          !!!next-input-character;          !!!next-input-character;
2831          redo A;          redo A;
2832        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2833          !!!cp (203);          !!!cp (203);
2834          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2835          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2836          !!!next-input-character;          !!!next-input-character;
2837          redo A;          redo A;
2838        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2839          !!!cp (204);          !!!cp (204);
2840          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2841          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2842          !!!next-input-character;          !!!next-input-character;
2843    
2844          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2845          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2846    
2847          redo A;          redo A;
2848        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2849          !!!cp (205);          !!!cp (205);
2850          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2851    
2852          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2853          ## reconsume          ## reconsume
2854    
2855          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2856          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2857    
2858          redo A;          redo A;
2859        } else {        } else {
2860          !!!cp (206);          !!!cp (206);
2861          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2862          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2863    
2864          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2865          !!!next-input-character;          !!!next-input-character;
2866          redo A;          redo A;
2867        }        }
2868      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2869        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2870          !!!cp (207);          !!!cp (207);
2871          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2872          !!!next-input-character;          !!!next-input-character;
2873          redo A;          redo A;
2874        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2875          !!!cp (208);          !!!cp (208);
2876          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2877    
2878          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2879          !!!next-input-character;          !!!next-input-character;
2880    
2881          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2882          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2883    
2884          redo A;          redo A;
2885        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2886          !!!cp (209);          !!!cp (209);
2887          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2888    
2889          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2890          ## reconsume          ## reconsume
2891    
2892          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2893          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2894    
2895          redo A;          redo A;
2896        } else {        } else {
2897          !!!cp (210);          !!!cp (210);
2898          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2899              .= chr $self->{next_char};              .= chr $self->{nc};
2900            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2901                                  length $self->{ct}->{sysid});
2902    
2903          ## Stay in the state          ## Stay in the state
2904          !!!next-input-character;          !!!next-input-character;
2905          redo A;          redo A;
2906        }        }
2907      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2908        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2909          !!!cp (211);          !!!cp (211);
2910          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2911          !!!next-input-character;          !!!next-input-character;
2912          redo A;          redo A;
2913        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2914          !!!cp (212);          !!!cp (212);
2915          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2916    
2917          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2918          !!!next-input-character;          !!!next-input-character;
2919    
2920          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2921          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2922    
2923          redo A;          redo A;
2924        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2925          !!!cp (213);          !!!cp (213);
2926          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2927    
2928          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2929          ## reconsume          ## reconsume
2930    
2931          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2932          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2933    
2934          redo A;          redo A;
2935        } else {        } else {
2936          !!!cp (214);          !!!cp (214);
2937          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2938              .= chr $self->{next_char};              .= chr $self->{nc};
2939            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2940                                  length $self->{ct}->{sysid});
2941    
2942          ## Stay in the state          ## Stay in the state
2943          !!!next-input-character;          !!!next-input-character;
2944          redo A;          redo A;
2945        }        }
2946      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2947        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2948          !!!cp (215);          !!!cp (215);
2949          ## Stay in the state          ## Stay in the state
2950          !!!next-input-character;          !!!next-input-character;
2951          redo A;          redo A;
2952        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2953          !!!cp (216);          !!!cp (216);
2954          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2955          !!!next-input-character;          !!!next-input-character;
2956    
2957          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2958    
2959          redo A;          redo A;
2960        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2961          !!!cp (217);          !!!cp (217);
2962          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2963          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2964          ## reconsume          ## reconsume
2965    
2966          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2967          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2968    
2969          redo A;          redo A;
2970        } else {        } else {
2971          !!!cp (218);          !!!cp (218);
2972          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2973          #$self->{current_token}->{quirks} = 1;          #$self->{ct}->{quirks} = 1;
2974    
2975          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2976          !!!next-input-character;          !!!next-input-character;
2977          redo A;          redo A;
2978        }        }
2979      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2980        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2981          !!!cp (219);          !!!cp (219);
2982          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2983          !!!next-input-character;          !!!next-input-character;
2984    
2985          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2986    
2987          redo A;          redo A;
2988        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2989          !!!cp (220);          !!!cp (220);
2990          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2991          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2992          ## reconsume          ## reconsume
2993    
2994          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2995    
2996          redo A;          redo A;
2997        } else {        } else {
2998          !!!cp (221);          !!!cp (221);
2999            my $s = '';
3000            $self->{read_until}->($s, q[>], 0);
3001    
3002          ## Stay in the state          ## Stay in the state
3003          !!!next-input-character;          !!!next-input-character;
3004          redo A;          redo A;
3005        }        }
3006      } elsif ($self->{state} == CDATA_BLOCK_STATE) {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
3007        my $s = '';        ## NOTE: "CDATA section state" in the state is jointly implemented
3008          ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3009          ## and |CDATA_SECTION_MSE2_STATE|.
3010                
3011        my ($l, $c) = ($self->{line}, $self->{column});        if ($self->{nc} == 0x005D) { # ]
3012            !!!cp (221.1);
3013        CS: while ($self->{next_char} != -1) {          $self->{state} = CDATA_SECTION_MSE1_STATE;
3014          if ($self->{next_char} == 0x005D) { # ]          !!!next-input-character;
3015            !!!next-input-character;          redo A;
3016            if ($self->{next_char} == 0x005D) { # ]        } elsif ($self->{nc} == -1) {
3017              !!!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};  
3018          !!!next-input-character;          !!!next-input-character;
3019        } # CS          if (length $self->{ct}->{data}) { # character
3020              !!!cp (221.2);
3021              !!!emit ($self->{ct}); # character
3022            } else {
3023              !!!cp (221.3);
3024              ## No token to emit. $self->{ct} is discarded.
3025            }        
3026            redo A;
3027          } else {
3028            !!!cp (221.4);
3029            $self->{ct}->{data} .= chr $self->{nc};
3030            $self->{read_until}->($self->{ct}->{data},
3031                                  q<]>,
3032                                  length $self->{ct}->{data});
3033    
3034        $self->{state} = DATA_STATE;          ## Stay in the state.
3035        ## next-input-character done or EOF, which is reconsumed.          !!!next-input-character;
3036            redo A;
3037          }
3038    
3039        if (length $s) {        ## ISSUE: "text tokens" in spec.
3040        } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3041          if ($self->{nc} == 0x005D) { # ]
3042            !!!cp (221.5);
3043            $self->{state} = CDATA_SECTION_MSE2_STATE;
3044            !!!next-input-character;
3045            redo A;
3046          } else {
3047          !!!cp (221.6);          !!!cp (221.6);
3048          !!!emit ({type => CHARACTER_TOKEN, data => $s,          $self->{ct}->{data} .= ']';
3049                    line => $l, column => $c});          $self->{state} = CDATA_SECTION_STATE;
3050            ## Reconsume.
3051            redo A;
3052          }
3053        } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3054          if ($self->{nc} == 0x003E) { # >
3055            $self->{state} = DATA_STATE;
3056            !!!next-input-character;
3057            if (length $self->{ct}->{data}) { # character
3058              !!!cp (221.7);
3059              !!!emit ($self->{ct}); # character
3060            } else {
3061              !!!cp (221.8);
3062              ## No token to emit. $self->{ct} is discarded.
3063            }
3064            redo A;
3065          } elsif ($self->{nc} == 0x005D) { # ]
3066            !!!cp (221.9); # character
3067            $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3068            ## Stay in the state.
3069            !!!next-input-character;
3070            redo A;
3071        } else {        } else {
3072          !!!cp (221.7);          !!!cp (221.11);
3073            $self->{ct}->{data} .= ']]'; # character
3074            $self->{state} = CDATA_SECTION_STATE;
3075            ## Reconsume.
3076            redo A;
3077          }
3078        } elsif ($self->{state} == ENTITY_STATE) {
3079          if ($is_space->{$self->{nc}} or
3080              {
3081                0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3082                $self->{entity_add} => 1,
3083              }->{$self->{nc}}) {
3084            !!!cp (1001);
3085            ## Don't consume
3086            ## No error
3087            ## Return nothing.
3088            #
3089          } elsif ($self->{nc} == 0x0023) { # #
3090            !!!cp (999);
3091            $self->{state} = ENTITY_HASH_STATE;
3092            $self->{s_kwd} = '#';
3093            !!!next-input-character;
3094            redo A;
3095          } elsif ((0x0041 <= $self->{nc} and
3096                    $self->{nc} <= 0x005A) or # A..Z
3097                   (0x0061 <= $self->{nc} and
3098                    $self->{nc} <= 0x007A)) { # a..z
3099            !!!cp (998);
3100            require Whatpm::_NamedEntityList;
3101            $self->{state} = ENTITY_NAME_STATE;
3102            $self->{s_kwd} = chr $self->{nc};
3103            $self->{entity__value} = $self->{s_kwd};
3104            $self->{entity__match} = 0;
3105            !!!next-input-character;
3106            redo A;
3107          } else {
3108            !!!cp (1027);
3109            !!!parse-error (type => 'bare ero');
3110            ## Return nothing.
3111            #
3112        }        }
3113    
3114        redo A;        ## NOTE: No character is consumed by the "consume a character
3115          ## reference" algorithm.  In other word, there is an "&" character
3116        ## ISSUE: "text tokens" in spec.        ## that does not introduce a character reference, which would be
3117        ## TODO: Streaming support        ## appended to the parent element or the attribute value in later
3118      } else {        ## process of the tokenizer.
3119        die "$0: $self->{state}: Unknown state";  
3120      }        if ($self->{prev_state} == DATA_STATE) {
3121    } # A            !!!cp (997);
3122            $self->{state} = $self->{prev_state};
3123    die "$0: _get_next_token: unexpected case";          ## Reconsume.
3124  } # _get_next_token          !!!emit ({type => CHARACTER_TOKEN, data => '&',
3125                      line => $self->{line_prev},
3126  sub _tokenize_attempt_to_consume_an_entity ($$$) {                    column => $self->{column_prev},
3127    my ($self, $in_attr, $additional) = @_;                   });
3128            redo A;
3129    my ($l, $c) = ($self->{line_prev}, $self->{column_prev});        } else {
3130            !!!cp (996);
3131            $self->{ca}->{value} .= '&';
3132            $self->{state} = $self->{prev_state};
3133            ## Reconsume.
3134            redo A;
3135          }
3136        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3137          if ($self->{nc} == 0x0078 or # x
3138              $self->{nc} == 0x0058) { # X
3139            !!!cp (995);
3140            $self->{state} = HEXREF_X_STATE;
3141            $self->{s_kwd} .= chr $self->{nc};
3142            !!!next-input-character;
3143            redo A;
3144          } elsif (0x0030 <= $self->{nc} and
3145                   $self->{nc} <= 0x0039) { # 0..9
3146            !!!cp (994);
3147            $self->{state} = NCR_NUM_STATE;
3148            $self->{s_kwd} = $self->{nc} - 0x0030;
3149            !!!next-input-character;
3150            redo A;
3151          } else {
3152            !!!parse-error (type => 'bare nero',
3153                            line => $self->{line_prev},
3154                            column => $self->{column_prev} - 1);
3155    
3156    if ({          ## NOTE: According to the spec algorithm, nothing is returned,
3157         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,          ## and then "&#" is appended to the parent element or the attribute
3158         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR          ## value in the later processing.
3159         $additional => 1,  
3160        }->{$self->{next_char}}) {          if ($self->{prev_state} == DATA_STATE) {
3161      !!!cp (1001);            !!!cp (1019);
3162      ## Don't consume            $self->{state} = $self->{prev_state};
3163      ## No error            ## Reconsume.
3164      return undef;            !!!emit ({type => CHARACTER_TOKEN,
3165    } elsif ($self->{next_char} == 0x0023) { # #                      data => '&#',
3166      !!!next-input-character;                      line => $self->{line_prev},
3167      if ($self->{next_char} == 0x0078 or # x                      column => $self->{column_prev} - 1,
3168          $self->{next_char} == 0x0058) { # X                     });
3169        my $code;            redo A;
       X: {  
         my $x_char = $self->{next_char};  
         !!!next-input-character;  
         if (0x0030 <= $self->{next_char} and  
             $self->{next_char} <= 0x0039) { # 0..9  
           !!!cp (1002);  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_char} - 0x0030;  
           redo X;  
         } elsif (0x0061 <= $self->{next_char} and  
                  $self->{next_char} <= 0x0066) { # a..f  
           !!!cp (1003);  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_char} - 0x0060 + 9;  
           redo X;  
         } elsif (0x0041 <= $self->{next_char} and  
                  $self->{next_char} <= 0x0046) { # A..F  
           !!!cp (1004);  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_char} - 0x0040 + 9;  
           redo X;  
         } elsif (not defined $code) { # no hexadecimal digit  
           !!!cp (1005);  
           !!!parse-error (type => 'bare hcro', line => $l, column => $c);  
           !!!back-next-input-character ($x_char, $self->{next_char});  
           $self->{next_char} = 0x0023; # #  
           return undef;  
         } elsif ($self->{next_char} == 0x003B) { # ;  
           !!!cp (1006);  
           !!!next-input-character;  
3170          } else {          } else {
3171            !!!cp (1007);            !!!cp (993);
3172            !!!parse-error (type => 'no refc', line => $l, column => $c);            $self->{ca}->{value} .= '&#';
3173              $self->{state} = $self->{prev_state};
3174              ## Reconsume.
3175              redo A;
3176          }          }
3177          }
3178          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {      } elsif ($self->{state} == NCR_NUM_STATE) {
3179            !!!cp (1008);        if (0x0030 <= $self->{nc} and
3180            !!!parse-error (type => 'invalid character reference',            $self->{nc} <= 0x0039) { # 0..9
                           text => (sprintf 'U+%04X', $code),  
                           line => $l, column => $c);  
           $code = 0xFFFD;  
         } elsif ($code > 0x10FFFF) {  
           !!!cp (1009);  
           !!!parse-error (type => 'invalid character reference',  
                           text => (sprintf '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 => 'C1 character reference', text => (sprintf 'U+%04X', $code), line => $l, column => $c);  
           $code = $c1_entity_char->{$code};  
         }  
   
         return {type => CHARACTER_TOKEN, data => chr $code,  
                 has_reference => 1,  
                 line => $l, column => $c,  
                };  
       } # X  
     } elsif (0x0030 <= $self->{next_char} and  
              $self->{next_char} <= 0x0039) { # 0..9  
       my $code = $self->{next_char} - 0x0030;  
       !!!next-input-character;  
         
       while (0x0030 <= $self->{next_char} and  
                 $self->{next_char} <= 0x0039) { # 0..9  
3181          !!!cp (1012);          !!!cp (1012);
3182          $code *= 10;          $self->{s_kwd} *= 10;
3183          $code += $self->{next_char} - 0x0030;          $self->{s_kwd} += $self->{nc} - 0x0030;
3184                    
3185            ## Stay in the state.
3186          !!!next-input-character;          !!!next-input-character;
3187        }          redo A;
3188          } elsif ($self->{nc} == 0x003B) { # ;
       if ($self->{next_char} == 0x003B) { # ;  
3189          !!!cp (1013);          !!!cp (1013);
3190          !!!next-input-character;          !!!next-input-character;
3191            #
3192        } else {        } else {
3193          !!!cp (1014);          !!!cp (1014);
3194          !!!parse-error (type => 'no refc', line => $l, column => $c);          !!!parse-error (type => 'no refc');
3195            ## Reconsume.
3196            #
3197        }        }
3198    
3199        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3200          my $l = $self->{line_prev};
3201          my $c = $self->{column_prev};
3202          if ($charref_map->{$code}) {
3203          !!!cp (1015);          !!!cp (1015);
3204          !!!parse-error (type => 'invalid character reference',          !!!parse-error (type => 'invalid character reference',
3205                          text => (sprintf 'U+%04X', $code),                          text => (sprintf 'U+%04X', $code),
3206                          line => $l, column => $c);                          line => $l, column => $c);
3207          $code = 0xFFFD;          $code = $charref_map->{$code};
3208        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3209          !!!cp (1016);          !!!cp (1016);
3210          !!!parse-error (type => 'invalid character reference',          !!!parse-error (type => 'invalid character reference',
3211                          text => (sprintf 'U-%08X', $code),                          text => (sprintf 'U-%08X', $code),
3212                          line => $l, column => $c);                          line => $l, column => $c);
3213          $code = 0xFFFD;          $code = 0xFFFD;
3214        } elsif ($code == 0x000D) {        }
3215          !!!cp (1017);  
3216          !!!parse-error (type => 'CR character reference',        if ($self->{prev_state} == DATA_STATE) {
3217                          line => $l, column => $c);          !!!cp (992);
3218          $code = 0x000A;          $self->{state} = $self->{prev_state};
3219        } elsif (0x80 <= $code and $code <= 0x9F) {          ## Reconsume.
3220          !!!cp (1018);          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3221          !!!parse-error (type => 'C1 character reference',                    line => $l, column => $c,
3222                     });
3223            redo A;
3224          } else {
3225            !!!cp (991);
3226            $self->{ca}->{value} .= chr $code;
3227            $self->{ca}->{has_reference} = 1;
3228            $self->{state} = $self->{prev_state};
3229            ## Reconsume.
3230            redo A;
3231          }
3232        } elsif ($self->{state} == HEXREF_X_STATE) {
3233          if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3234              (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3235              (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3236            # 0..9, A..F, a..f
3237            !!!cp (990);
3238            $self->{state} = HEXREF_HEX_STATE;
3239            $self->{s_kwd} = 0;
3240            ## Reconsume.
3241            redo A;
3242          } else {
3243            !!!parse-error (type => 'bare hcro',
3244                            line => $self->{line_prev},
3245                            column => $self->{column_prev} - 2);
3246    
3247            ## NOTE: According to the spec algorithm, nothing is returned,
3248            ## and then "&#" followed by "X" or "x" is appended to the parent
3249            ## element or the attribute value in the later processing.
3250    
3251            if ($self->{prev_state} == DATA_STATE) {
3252              !!!cp (1005);
3253              $self->{state} = $self->{prev_state};
3254              ## Reconsume.
3255              !!!emit ({type => CHARACTER_TOKEN,
3256                        data => '&' . $self->{s_kwd},
3257                        line => $self->{line_prev},
3258                        column => $self->{column_prev} - length $self->{s_kwd},
3259                       });
3260              redo A;
3261            } else {
3262              !!!cp (989);
3263              $self->{ca}->{value} .= '&' . $self->{s_kwd};
3264              $self->{state} = $self->{prev_state};
3265              ## Reconsume.
3266              redo A;
3267            }
3268          }
3269        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3270          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3271            # 0..9
3272            !!!cp (1002);
3273            $self->{s_kwd} *= 0x10;
3274            $self->{s_kwd} += $self->{nc} - 0x0030;
3275            ## Stay in the state.
3276            !!!next-input-character;
3277            redo A;
3278          } elsif (0x0061 <= $self->{nc} and
3279                   $self->{nc} <= 0x0066) { # a..f
3280            !!!cp (1003);
3281            $self->{s_kwd} *= 0x10;
3282            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3283            ## Stay in the state.
3284            !!!next-input-character;
3285            redo A;
3286          } elsif (0x0041 <= $self->{nc} and
3287                   $self->{nc} <= 0x0046) { # A..F
3288            !!!cp (1004);
3289            $self->{s_kwd} *= 0x10;
3290            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3291            ## Stay in the state.
3292            !!!next-input-character;
3293            redo A;
3294          } elsif ($self->{nc} == 0x003B) { # ;
3295            !!!cp (1006);
3296            !!!next-input-character;
3297            #
3298          } else {
3299            !!!cp (1007);
3300            !!!parse-error (type => 'no refc',
3301                            line => $self->{line},
3302                            column => $self->{column});
3303            ## Reconsume.
3304            #
3305          }
3306    
3307          my $code = $self->{s_kwd};
3308          my $l = $self->{line_prev};
3309          my $c = $self->{column_prev};
3310          if ($charref_map->{$code}) {
3311            !!!cp (1008);
3312            !!!parse-error (type => 'invalid character reference',
3313                          text => (sprintf 'U+%04X', $code),                          text => (sprintf 'U+%04X', $code),
3314                          line => $l, column => $c);                          line => $l, column => $c);
3315          $code = $c1_entity_char->{$code};          $code = $charref_map->{$code};
3316          } elsif ($code > 0x10FFFF) {
3317            !!!cp (1009);
3318            !!!parse-error (type => 'invalid character reference',
3319                            text => (sprintf 'U-%08X', $code),
3320                            line => $l, column => $c);
3321            $code = 0xFFFD;
3322        }        }
3323          
3324        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1,        if ($self->{prev_state} == DATA_STATE) {
3325                line => $l, column => $c,          !!!cp (988);
3326               };          $self->{state} = $self->{prev_state};
3327      } else {          ## Reconsume.
3328        !!!cp (1019);          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3329        !!!parse-error (type => 'bare nero', line => $l, column => $c);                    line => $l, column => $c,
3330        !!!back-next-input-character ($self->{next_char});                   });
3331        $self->{next_char} = 0x0023; # #          redo A;
3332        return undef;        } else {
3333      }          !!!cp (987);
3334    } elsif ((0x0041 <= $self->{next_char} and          $self->{ca}->{value} .= chr $code;
3335              $self->{next_char} <= 0x005A) or          $self->{ca}->{has_reference} = 1;
3336             (0x0061 <= $self->{next_char} and          $self->{state} = $self->{prev_state};
3337              $self->{next_char} <= 0x007A)) {          ## Reconsume.
3338      my $entity_name = chr $self->{next_char};          redo A;
3339      !!!next-input-character;        }
3340        } elsif ($self->{state} == ENTITY_NAME_STATE) {
3341      my $value = $entity_name;        if (length $self->{s_kwd} < 30 and
3342      my $match = 0;            ## NOTE: Some number greater than the maximum length of entity name
3343      require Whatpm::_NamedEntityList;            ((0x0041 <= $self->{nc} and # a
3344      our $EntityChar;              $self->{nc} <= 0x005A) or # x
3345               (0x0061 <= $self->{nc} and # a
3346      while (length $entity_name < 30 and              $self->{nc} <= 0x007A) or # z
3347             ## NOTE: Some number greater than the maximum length of entity name             (0x0030 <= $self->{nc} and # 0
3348             ((0x0041 <= $self->{next_char} and # a              $self->{nc} <= 0x0039) or # 9
3349               $self->{next_char} <= 0x005A) or # x             $self->{nc} == 0x003B)) { # ;
3350              (0x0061 <= $self->{next_char} and # a          our $EntityChar;
3351               $self->{next_char} <= 0x007A) or # z          $self->{s_kwd} .= chr $self->{nc};
3352              (0x0030 <= $self->{next_char} and # 0          if (defined $EntityChar->{$self->{s_kwd}}) {
3353               $self->{next_char} <= 0x0039) or # 9            if ($self->{nc} == 0x003B) { # ;
3354              $self->{next_char} == 0x003B)) { # ;              !!!cp (1020);
3355        $entity_name .= chr $self->{next_char};              $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3356        if (defined $EntityChar->{$entity_name}) {              $self->{entity__match} = 1;
3357          if ($self->{next_char} == 0x003B) { # ;              !!!next-input-character;
3358            !!!cp (1020);              #
3359            $value = $EntityChar->{$entity_name};            } else {
3360            $match = 1;              !!!cp (1021);
3361            !!!next-input-character;              $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3362            last;              $self->{entity__match} = -1;
3363                ## Stay in the state.
3364                !!!next-input-character;
3365                redo A;
3366              }
3367          } else {          } else {
3368            !!!cp (1021);            !!!cp (1022);
3369            $value = $EntityChar->{$entity_name};            $self->{entity__value} .= chr $self->{nc};
3370            $match = -1;            $self->{entity__match} *= 2;
3371              ## Stay in the state.
3372            !!!next-input-character;            !!!next-input-character;
3373              redo A;
3374            }
3375          }
3376    
3377          my $data;
3378          my $has_ref;
3379          if ($self->{entity__match} > 0) {
3380            !!!cp (1023);
3381            $data = $self->{entity__value};
3382            $has_ref = 1;
3383            #
3384          } elsif ($self->{entity__match} < 0) {
3385            !!!parse-error (type => 'no refc');
3386            if ($self->{prev_state} != DATA_STATE and # in attribute
3387                $self->{entity__match} < -1) {
3388              !!!cp (1024);
3389              $data = '&' . $self->{s_kwd};
3390              #
3391            } else {
3392              !!!cp (1025);
3393              $data = $self->{entity__value};
3394              $has_ref = 1;
3395              #
3396          }          }
3397        } else {        } else {
3398          !!!cp (1022);          !!!cp (1026);
3399          $value .= chr $self->{next_char};          !!!parse-error (type => 'bare ero',
3400          $match *= 2;                          line => $self->{line_prev},
3401          !!!next-input-character;                          column => $self->{column_prev} - length $self->{s_kwd});
3402            $data = '&' . $self->{s_kwd};
3403            #
3404        }        }
3405      }    
3406              ## NOTE: In these cases, when a character reference is found,
3407      if ($match > 0) {        ## it is consumed and a character token is returned, or, otherwise,
3408        !!!cp (1023);        ## nothing is consumed and returned, according to the spec algorithm.
3409        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,        ## In this implementation, anything that has been examined by the
3410                line => $l, column => $c,        ## tokenizer is appended to the parent element or the attribute value
3411               };        ## as string, either literal string when no character reference or
3412      } elsif ($match < 0) {        ## entity-replaced string otherwise, in this stage, since any characters
3413        !!!parse-error (type => 'no refc', line => $l, column => $c);        ## that would not be consumed are appended in the data state or in an
3414        if ($in_attr and $match < -1) {        ## appropriate attribute value state anyway.
3415          !!!cp (1024);  
3416          return {type => CHARACTER_TOKEN, data => '&'.$entity_name,        if ($self->{prev_state} == DATA_STATE) {
3417                  line => $l, column => $c,          !!!cp (986);
3418                 };          $self->{state} = $self->{prev_state};
3419        } else {          ## Reconsume.
3420          !!!cp (1025);          !!!emit ({type => CHARACTER_TOKEN,
3421          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,                    data => $data,
3422                  line => $l, column => $c,                    line => $self->{line_prev},
3423                 };                    column => $self->{column_prev} + 1 - length $self->{s_kwd},
3424                     });
3425            redo A;
3426          } else {
3427            !!!cp (985);
3428            $self->{ca}->{value} .= $data;
3429            $self->{ca}->{has_reference} = 1 if $has_ref;
3430            $self->{state} = $self->{prev_state};
3431            ## Reconsume.
3432            redo A;
3433        }        }
3434      } else {      } else {
3435        !!!cp (1026);        die "$0: $self->{state}: Unknown state";
       !!!parse-error (type => 'bare ero', line => $l, column => $c);  
       ## NOTE: "No characters are consumed" in the spec.  
       return {type => CHARACTER_TOKEN, data => '&'.$value,  
               line => $l, column => $c,  
              };  
3436      }      }
3437    } else {    } # A  
3438      !!!cp (1027);  
3439      ## no characters are consumed    die "$0: _get_next_token: unexpected case";
3440      !!!parse-error (type => 'bare ero', line => $l, column => $c);  } # _get_next_token
     return undef;  
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3441    
3442  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3443    my $self = shift;    my $self = shift;
# Line 3157  sub _construct_tree ($) { Line 3476  sub _construct_tree ($) {
3476    
3477    undef $self->{form_element};    undef $self->{form_element};
3478    undef $self->{head_element};    undef $self->{head_element};
3479      undef $self->{head_element_inserted};
3480    $self->{open_elements} = [];    $self->{open_elements} = [];
3481    undef $self->{inner_html_node};    undef $self->{inner_html_node};
3482    
# Line 3185  sub _tree_construction_initial ($) { Line 3505  sub _tree_construction_initial ($) {
3505        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3506        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3507        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3508            defined $token->{system_identifier}) {            defined $token->{sysid}) {
3509          !!!cp ('t1');          !!!cp ('t1');
3510          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
3511        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3512          !!!cp ('t2');          !!!cp ('t2');
3513          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
3514        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3515          if ($token->{public_identifier} eq 'XSLT-compat') {          if ($token->{pubid} eq 'XSLT-compat') {
3516            !!!cp ('t1.2');            !!!cp ('t1.2');
3517            !!!parse-error (type => 'XSLT-compat', token => $token,            !!!parse-error (type => 'XSLT-compat', token => $token,
3518                            level => $self->{level}->{should});                            level => $self->{level}->{should});
# Line 3208  sub _tree_construction_initial ($) { Line 3528  sub _tree_construction_initial ($) {
3528          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3529        ## NOTE: Default value for both |public_id| and |system_id| attributes        ## NOTE: Default value for both |public_id| and |system_id| attributes
3530        ## 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.
3531        $doctype->public_id ($token->{public_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3532            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};  
3533        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3534        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3535        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
# Line 3219  sub _tree_construction_initial ($) { Line 3537  sub _tree_construction_initial ($) {
3537        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3538          !!!cp ('t4');          !!!cp ('t4');
3539          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3540        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3541          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3542          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3543          my $prefix = [          my $prefix = [
3544            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
# Line 3294  sub _tree_construction_initial ($) { Line 3612  sub _tree_construction_initial ($) {
3612            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3613          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3614                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3615            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3616              !!!cp ('t6');              !!!cp ('t6');
3617              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3618            } else {            } else {
# Line 3311  sub _tree_construction_initial ($) { Line 3629  sub _tree_construction_initial ($) {
3629        } else {        } else {
3630          !!!cp ('t10');          !!!cp ('t10');
3631        }        }
3632        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3633          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3634          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3635          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") {
3636            ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is            ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
# Line 3342  sub _tree_construction_initial ($) { Line 3660  sub _tree_construction_initial ($) {
3660        !!!ack-later;        !!!ack-later;
3661        return;        return;
3662      } elsif ($token->{type} == CHARACTER_TOKEN) {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3663        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3664          ## Ignore the token          ## Ignore the token
3665    
3666          unless (length $token->{data}) {          unless (length $token->{data}) {
# Line 3399  sub _tree_construction_root_element ($) Line 3717  sub _tree_construction_root_element ($)
3717          !!!next-token;          !!!next-token;
3718          redo B;          redo B;
3719        } elsif ($token->{type} == CHARACTER_TOKEN) {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3720          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3721            ## Ignore the token.            ## Ignore the token.
3722    
3723            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 3466  sub _tree_construction_root_element ($) Line 3784  sub _tree_construction_root_element ($)
3784      ## NOTE: Reprocess the token.      ## NOTE: Reprocess the token.
3785      !!!ack-later;      !!!ack-later;
3786      return; ## Go to the "before head" insertion mode.      return; ## Go to the "before head" insertion mode.
   
     ## ISSUE: There is an issue in the spec  
3787    } # B    } # B
3788    
3789    die "$0: _tree_construction_root_element: This should never be reached";    die "$0: _tree_construction_root_element: This should never be reached";
# Line 3768  sub _tree_construction_main ($) { Line 4084  sub _tree_construction_main ($) {
4084    
4085    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4086    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4087      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
4088    my $open_tables = [[$self->{open_elements}->[0]->[0]]];    my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4089    
4090    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
# Line 4041  sub _tree_construction_main ($) { Line 4358  sub _tree_construction_main ($) {
4358      }      }
4359    }; # $insert_to_foster    }; # $insert_to_foster
4360    
4361      ## NOTE: When a character is inserted, if the last node that was
4362      ## inserted by the parser is a Text node and the character has to be
4363      ## inserted after that node, then the character is appended to the
4364      ## Text node.  However, if any other node is inserted by the parser,
4365      ## then a new Text node is created and the character is appended as
4366      ## that Text node.  If I'm not wrong, there are only two cases where
4367      ## this occurs.  One is the case where an element node is inserted
4368      ## to the |head| element.  This is covered by using the
4369      ## |$self->{head_element_inserted}| flag.  Another is the case where
4370      ## an element or comment is inserted into the |table| subtree while
4371      ## foster parenting happens.  This is covered by using the [2] flag
4372      ## of the |$open_tables| structure.  All other cases are handled
4373      ## simply by calling |manakai_append_text| method.
4374    
4375    B: while (1) {    B: while (1) {
4376      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
4377        !!!cp ('t73');        !!!cp ('t73');
# Line 4088  sub _tree_construction_main ($) { Line 4419  sub _tree_construction_main ($) {
4419        } else {        } else {
4420          !!!cp ('t87');          !!!cp ('t87');
4421          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
4422            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
4423        }        }
4424        !!!next-token;        !!!next-token;
4425        next B;        next B;
# Line 4203  sub _tree_construction_main ($) { Line 4535  sub _tree_construction_main ($) {
4535          pop @{$self->{open_elements}}          pop @{$self->{open_elements}}
4536              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4537    
4538            ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
4539    
4540          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4541          ## Reprocess.          ## Reprocess.
4542          next B;          next B;
# Line 4213  sub _tree_construction_main ($) { Line 4547  sub _tree_construction_main ($) {
4547    
4548      if ($self->{insertion_mode} & HEAD_IMS) {      if ($self->{insertion_mode} & HEAD_IMS) {
4549        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4550          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4551            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4552              !!!cp ('t88.2');              if ($self->{head_element_inserted}) {
4553              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                !!!cp ('t88.3');
4554                  $self->{open_elements}->[-1]->[0]->append_child
4555                    ($self->{document}->create_text_node ($1));
4556                  delete $self->{head_element_inserted};
4557                  ## NOTE: |</head> <link> |
4558                  #
4559                } else {
4560                  !!!cp ('t88.2');
4561                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4562                  ## NOTE: |</head> &#x20;|
4563                  #
4564                }
4565            } else {            } else {
4566              !!!cp ('t88.1');              !!!cp ('t88.1');
4567              ## Ignore the token.              ## Ignore the token.
4568              !!!next-token;              #
             next B;  
4569            }            }
4570            unless (length $token->{data}) {            unless (length $token->{data}) {
4571              !!!cp ('t88');              !!!cp ('t88');
4572              !!!next-token;              !!!next-token;
4573              next B;              next B;
4574            }            }
4575    ## TODO: set $token->{column} appropriately
4576          }          }
4577    
4578          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
# Line 4312  sub _tree_construction_main ($) { Line 4657  sub _tree_construction_main ($) {
4657            !!!cp ('t97');            !!!cp ('t97');
4658          }          }
4659    
4660              if ($token->{tag_name} eq 'base') {          if ($token->{tag_name} eq 'base') {
4661                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4662                  !!!cp ('t98');              !!!cp ('t98');
4663                  ## As if </noscript>              ## As if </noscript>
4664                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4665                  !!!parse-error (type => 'in noscript', text => 'base',              !!!parse-error (type => 'in noscript', text => 'base',
4666                                  token => $token);                              token => $token);
4667                            
4668                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
4669                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
4670                } else {            } else {
4671                  !!!cp ('t99');              !!!cp ('t99');
4672                }            }
4673    
4674                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4675                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4676                  !!!cp ('t100');              !!!cp ('t100');
4677                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
4678                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
4679                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
4680                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
4681                } else {              $self->{head_element_inserted} = 1;
4682                  !!!cp ('t101');            } else {
4683                }              !!!cp ('t101');
4684                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            }
4685                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4686                pop @{$self->{open_elements}} # <head>            pop @{$self->{open_elements}};
4687                    if $self->{insertion_mode} == AFTER_HEAD_IM;            pop @{$self->{open_elements}} # <head>
4688                !!!nack ('t101.1');                if $self->{insertion_mode} == AFTER_HEAD_IM;
4689                !!!next-token;            !!!nack ('t101.1');
4690                next B;            !!!next-token;
4691              } elsif ($token->{tag_name} eq 'link') {            next B;
4692                ## NOTE: There is a "as if in head" code clone.          } elsif ($token->{tag_name} eq 'link') {
4693                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            ## NOTE: There is a "as if in head" code clone.
4694                  !!!cp ('t102');            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4695                  !!!parse-error (type => 'after head',              !!!cp ('t102');
4696                                  text => $token->{tag_name}, token => $token);              !!!parse-error (type => 'after head',
4697                  push @{$self->{open_elements}},                              text => $token->{tag_name}, token => $token);
4698                      [$self->{head_element}, $el_category->{head}];              push @{$self->{open_elements}},
4699                } else {                  [$self->{head_element}, $el_category->{head}];
4700                  !!!cp ('t103');              $self->{head_element_inserted} = 1;
4701                }            } else {
4702                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);              !!!cp ('t103');
4703                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            }
4704                pop @{$self->{open_elements}} # <head>            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4705                    if $self->{insertion_mode} == AFTER_HEAD_IM;            pop @{$self->{open_elements}};
4706                !!!ack ('t103.1');            pop @{$self->{open_elements}} # <head>
4707                !!!next-token;                if $self->{insertion_mode} == AFTER_HEAD_IM;
4708                next B;            !!!ack ('t103.1');
4709              } elsif ($token->{tag_name} eq 'meta') {            !!!next-token;
4710                ## NOTE: There is a "as if in head" code clone.            next B;
4711                if ($self->{insertion_mode} == AFTER_HEAD_IM) {          } elsif ($token->{tag_name} eq 'command' or
4712                  !!!cp ('t104');                   $token->{tag_name} eq 'eventsource') {
4713                  !!!parse-error (type => 'after head',            if ($self->{insertion_mode} == IN_HEAD_IM) {
4714                                  text => $token->{tag_name}, token => $token);              ## NOTE: If the insertion mode at the time of the emission
4715                  push @{$self->{open_elements}},              ## of the token was "before head", $self->{insertion_mode}
4716                      [$self->{head_element}, $el_category->{head}];              ## is already changed to |IN_HEAD_IM|.
4717                } else {  
4718                  !!!cp ('t105');              ## NOTE: There is a "as if in head" code clone.
4719                }              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4720                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);              pop @{$self->{open_elements}};
4721                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.              pop @{$self->{open_elements}} # <head>
4722                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4723                !!!ack ('t103.2');
4724                !!!next-token;
4725                next B;
4726              } else {
4727                ## NOTE: "in head noscript" or "after head" insertion mode
4728                ## - in these cases, these tags are treated as same as
4729                ## normal in-body tags.
4730                !!!cp ('t103.3');
4731                #
4732              }
4733            } elsif ($token->{tag_name} eq 'meta') {
4734              ## NOTE: There is a "as if in head" code clone.
4735              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4736                !!!cp ('t104');
4737                !!!parse-error (type => 'after head',
4738                                text => $token->{tag_name}, token => $token);
4739                push @{$self->{open_elements}},
4740                    [$self->{head_element}, $el_category->{head}];
4741                $self->{head_element_inserted} = 1;
4742              } else {
4743                !!!cp ('t105');
4744              }
4745              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4746              my $meta_el = pop @{$self->{open_elements}};
4747    
4748                unless ($self->{confident}) {                unless ($self->{confident}) {
4749                  if ($token->{attributes}->{charset}) {                  if ($token->{attributes}->{charset}) {
# Line 4391  sub _tree_construction_main ($) { Line 4761  sub _tree_construction_main ($) {
4761                  } elsif ($token->{attributes}->{content}) {                  } elsif ($token->{attributes}->{content}) {
4762                    if ($token->{attributes}->{content}->{value}                    if ($token->{attributes}->{content}->{value}
4763                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4764                            [\x09-\x0D\x20]*=                            [\x09\x0A\x0C\x0D\x20]*=
4765                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4766                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20\x3B]*))/x) {                            ([^"'\x09\x0A\x0C\x0D\x20]
4767                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4768                      !!!cp ('t107');                      !!!cp ('t107');
4769                      ## NOTE: Whether the encoding is supported or not is handled                      ## NOTE: Whether the encoding is supported or not is handled
4770                      ## in the {change_encoding} callback.                      ## in the {change_encoding} callback.
# Line 4430  sub _tree_construction_main ($) { Line 4801  sub _tree_construction_main ($) {
4801                !!!ack ('t110.1');                !!!ack ('t110.1');
4802                !!!next-token;                !!!next-token;
4803                next B;                next B;
4804              } elsif ($token->{tag_name} eq 'title') {          } elsif ($token->{tag_name} eq 'title') {
4805                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4806                  !!!cp ('t111');              !!!cp ('t111');
4807                  ## As if </noscript>              ## As if </noscript>
4808                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4809                  !!!parse-error (type => 'in noscript', text => 'title',              !!!parse-error (type => 'in noscript', text => 'title',
4810                                  token => $token);                              token => $token);
4811                            
4812                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
4813                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
4814                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4815                  !!!cp ('t112');              !!!cp ('t112');
4816                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
4817                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
4818                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
4819                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
4820                } else {              $self->{head_element_inserted} = 1;
4821                  !!!cp ('t113');            } else {
4822                }              !!!cp ('t113');
4823              }
4824    
4825                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4826                my $parent = defined $self->{head_element} ? $self->{head_element}            $parse_rcdata->(RCDATA_CONTENT_MODEL);
4827                    : $self->{open_elements}->[-1]->[0];            pop @{$self->{open_elements}} # <head>
4828                $parse_rcdata->(RCDATA_CONTENT_MODEL);                if $self->{insertion_mode} == AFTER_HEAD_IM;
4829                pop @{$self->{open_elements}} # <head>            next B;
4830                    if $self->{insertion_mode} == AFTER_HEAD_IM;          } elsif ($token->{tag_name} eq 'style' or
4831                next B;                   $token->{tag_name} eq 'noframes') {
4832              } elsif ($token->{tag_name} eq 'style' or            ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4833                       $token->{tag_name} eq 'noframes') {            ## insertion mode IN_HEAD_IM)
4834                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and            ## NOTE: There is a "as if in head" code clone.
4835                ## insertion mode IN_HEAD_IM)            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4836                ## NOTE: There is a "as if in head" code clone.              !!!cp ('t114');
4837                if ($self->{insertion_mode} == AFTER_HEAD_IM) {              !!!parse-error (type => 'after head',
4838                  !!!cp ('t114');                              text => $token->{tag_name}, token => $token);
4839                  !!!parse-error (type => 'after head',              push @{$self->{open_elements}},
4840                                  text => $token->{tag_name}, token => $token);                  [$self->{head_element}, $el_category->{head}];
4841                  push @{$self->{open_elements}},              $self->{head_element_inserted} = 1;
4842                      [$self->{head_element}, $el_category->{head}];            } else {
4843                } else {              !!!cp ('t115');
4844                  !!!cp ('t115');            }
4845                }            $parse_rcdata->(CDATA_CONTENT_MODEL);
4846                $parse_rcdata->(CDATA_CONTENT_MODEL);            pop @{$self->{open_elements}} # <head>
4847                pop @{$self->{open_elements}} # <head>                if $self->{insertion_mode} == AFTER_HEAD_IM;
4848                    if $self->{insertion_mode} == AFTER_HEAD_IM;            next B;
               next B;  
4849              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
4850                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
4851                  !!!cp ('t116');                  !!!cp ('t116');
# Line 4496  sub _tree_construction_main ($) { Line 4867  sub _tree_construction_main ($) {
4867                  !!!cp ('t118');                  !!!cp ('t118');
4868                  #                  #
4869                }                }
4870              } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
4871                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4872                  !!!cp ('t119');              !!!cp ('t119');
4873                  ## As if </noscript>              ## As if </noscript>
4874                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4875                  !!!parse-error (type => 'in noscript', text => 'script',              !!!parse-error (type => 'in noscript', text => 'script',
4876                                  token => $token);                              token => $token);
4877                            
4878                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
4879                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
4880                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4881                  !!!cp ('t120');              !!!cp ('t120');
4882                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
4883                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
4884                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
4885                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
4886                } else {              $self->{head_element_inserted} = 1;
4887                  !!!cp ('t121');            } else {
4888                }              !!!cp ('t121');
4889              }
4890    
4891                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4892                $script_start_tag->();            $script_start_tag->();
4893                pop @{$self->{open_elements}} # <head>            pop @{$self->{open_elements}} # <head>
4894                    if $self->{insertion_mode} == AFTER_HEAD_IM;                if $self->{insertion_mode} == AFTER_HEAD_IM;
4895                next B;            next B;
4896              } elsif ($token->{tag_name} eq 'body' or          } elsif ($token->{tag_name} eq 'body' or
4897                       $token->{tag_name} eq 'frameset') {                   $token->{tag_name} eq 'frameset') {
4898                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4899                  !!!cp ('t122');                  !!!cp ('t122');
4900                  ## As if </noscript>                  ## As if </noscript>
# Line 4829  sub _tree_construction_main ($) { Line 5201  sub _tree_construction_main ($) {
5201        } else {        } else {
5202          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5203        }        }
   
           ## ISSUE: An issue in the spec.  
5204      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
5205            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
5206              !!!cp ('t150');              !!!cp ('t150');
# Line 5202  sub _tree_construction_main ($) { Line 5572  sub _tree_construction_main ($) {
5572      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
5573        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
5574          if (not $open_tables->[-1]->[1] and # tainted          if (not $open_tables->[-1]->[1] and # tainted
5575              $token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5576            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5577                                
5578            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 5216  sub _tree_construction_main ($) { Line 5586  sub _tree_construction_main ($) {
5586    
5587          !!!parse-error (type => 'in table:#text', token => $token);          !!!parse-error (type => 'in table:#text', token => $token);
5588    
5589              ## As if in body, but insert into foster parent element          ## NOTE: As if in body, but insert into the foster parent element.
5590              ## ISSUE: Spec says that "whenever a node would be inserted          $reconstruct_active_formatting_elements->($insert_to_foster);
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
5591                            
5592              if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {          if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
5593                # MUST            # MUST
5594                my $foster_parent_element;            my $foster_parent_element;
5595                my $next_sibling;            my $next_sibling;
5596                my $prev_sibling;            my $prev_sibling;
5597                OE: for (reverse 0..$#{$self->{open_elements}}) {            OE: for (reverse 0..$#{$self->{open_elements}}) {
5598                  if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5599                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5600                    if (defined $parent and $parent->node_type == 1) {                if (defined $parent and $parent->node_type == 1) {
5601                      !!!cp ('t196');                  $foster_parent_element = $parent;
5602                      $foster_parent_element = $parent;                  !!!cp ('t196');
5603                      $next_sibling = $self->{open_elements}->[$_]->[0];                  $next_sibling = $self->{open_elements}->[$_]->[0];
5604                      $prev_sibling = $next_sibling->previous_sibling;                  $prev_sibling = $next_sibling->previous_sibling;
5605                    } else {                  #
                     !!!cp ('t197');  
                     $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];  
                     $prev_sibling = $foster_parent_element->last_child;  
                   }  
                   last OE;  
                 }  
               } # OE  
               $foster_parent_element = $self->{open_elements}->[0]->[0] and  
               $prev_sibling = $foster_parent_element->last_child  
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 !!!cp ('t198');  
                 $prev_sibling->manakai_append_text ($token->{data});  
5606                } else {                } else {
5607                  !!!cp ('t199');                  !!!cp ('t197');
5608                  $foster_parent_element->insert_before                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5609                    ($self->{document}->create_text_node ($token->{data}),                  $prev_sibling = $foster_parent_element->last_child;
5610                     $next_sibling);                  #
5611                }                }
5612                  last OE;
5613                }
5614              } # OE
5615              $foster_parent_element = $self->{open_elements}->[0]->[0] and
5616              $prev_sibling = $foster_parent_element->last_child
5617                  unless defined $foster_parent_element;
5618              undef $prev_sibling unless $open_tables->[-1]->[2]; # ~node inserted
5619              if (defined $prev_sibling and
5620                  $prev_sibling->node_type == 3) {
5621                !!!cp ('t198');
5622                $prev_sibling->manakai_append_text ($token->{data});
5623              } else {
5624                !!!cp ('t199');
5625                $foster_parent_element->insert_before
5626                    ($self->{document}->create_text_node ($token->{data}),
5627                     $next_sibling);
5628              }
5629            $open_tables->[-1]->[1] = 1; # tainted            $open_tables->[-1]->[1] = 1; # tainted
5630              $open_tables->[-1]->[2] = 1; # ~node inserted
5631          } else {          } else {
5632              ## NOTE: Fragment case or in a foster parent'ed element
5633              ## (e.g. |<table><span>a|).  In fragment case, whether the
5634              ## character is appended to existing node or a new node is
5635              ## created is irrelevant, since the foster parent'ed nodes
5636              ## are discarded and fragment parsing does not invoke any
5637              ## script.
5638            !!!cp ('t200');            !!!cp ('t200');
5639            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});            $self->{open_elements}->[-1]->[0]->manakai_append_text
5640                  ($token->{data});
5641          }          }
5642                            
5643          !!!next-token;          !!!next-token;
# Line 5296  sub _tree_construction_main ($) { Line 5674  sub _tree_construction_main ($) {
5674                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5675              }              }
5676                                    
5677                  $self->{insertion_mode} = IN_ROW_IM;              $self->{insertion_mode} = IN_ROW_IM;
5678                  if ($token->{tag_name} eq 'tr') {              if ($token->{tag_name} eq 'tr') {
5679                    !!!cp ('t204');                !!!cp ('t204');
5680                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5681                    !!!nack ('t204');                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5682                    !!!next-token;                !!!nack ('t204');
5683                    next B;                !!!next-token;
5684                  } else {                next B;
5685                    !!!cp ('t205');              } else {
5686                    !!!insert-element ('tr',, $token);                !!!cp ('t205');
5687                    ## reprocess in the "in row" insertion mode                !!!insert-element ('tr',, $token);
5688                  }                ## reprocess in the "in row" insertion mode
5689                } else {              }
5690                  !!!cp ('t206');            } else {
5691                }              !!!cp ('t206');
5692              }
5693    
5694                ## Clear back to table row context                ## Clear back to table row context
5695                while (not ($self->{open_elements}->[-1]->[1]                while (not ($self->{open_elements}->[-1]->[1]
# Line 5319  sub _tree_construction_main ($) { Line 5698  sub _tree_construction_main ($) {
5698                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5699                }                }
5700                                
5701                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5702                $self->{insertion_mode} = IN_CELL_IM;            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5703              $self->{insertion_mode} = IN_CELL_IM;
5704    
5705                push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
5706                                
5707                !!!nack ('t207.1');            !!!nack ('t207.1');
5708              !!!next-token;
5709              next B;
5710            } elsif ({
5711                      caption => 1, col => 1, colgroup => 1,
5712                      tbody => 1, tfoot => 1, thead => 1,
5713                      tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5714                     }->{$token->{tag_name}}) {
5715              if ($self->{insertion_mode} == IN_ROW_IM) {
5716                ## As if </tr>
5717                ## have an element in table scope
5718                my $i;
5719                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5720                  my $node = $self->{open_elements}->[$_];
5721                  if ($node->[1] & TABLE_ROW_EL) {
5722                    !!!cp ('t208');
5723                    $i = $_;
5724                    last INSCOPE;
5725                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5726                    !!!cp ('t209');
5727                    last INSCOPE;
5728                  }
5729                } # INSCOPE
5730                unless (defined $i) {
5731                  !!!cp ('t210');
5732                  ## TODO: This type is wrong.
5733                  !!!parse-error (type => 'unmacthed end tag',
5734                                  text => $token->{tag_name}, token => $token);
5735                  ## Ignore the token
5736                  !!!nack ('t210.1');
5737                !!!next-token;                !!!next-token;
5738                next B;                next B;
5739              } elsif ({              }
                       caption => 1, col => 1, colgroup => 1,  
                       tbody => 1, tfoot => 1, thead => 1,  
                       tr => 1, # $self->{insertion_mode} == IN_ROW_IM  
                      }->{$token->{tag_name}}) {  
               if ($self->{insertion_mode} == IN_ROW_IM) {  
                 ## As if </tr>  
                 ## have an element in table scope  
                 my $i;  
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                   my $node = $self->{open_elements}->[$_];  
                   if ($node->[1] & TABLE_ROW_EL) {  
                     !!!cp ('t208');  
                     $i = $_;  
                     last INSCOPE;  
                   } elsif ($node->[1] & TABLE_SCOPING_EL) {  
                     !!!cp ('t209');  
                     last INSCOPE;  
                   }  
                 } # INSCOPE  
                 unless (defined $i) {  
                   !!!cp ('t210');  
 ## TODO: This type is wrong.  
                   !!!parse-error (type => 'unmacthed end tag',  
                                   text => $token->{tag_name}, token => $token);  
                   ## Ignore the token  
                   !!!nack ('t210.1');  
                   !!!next-token;  
                   next B;  
                 }  
5740                                    
5741                  ## Clear back to table row context                  ## Clear back to table row context
5742                  while (not ($self->{open_elements}->[-1]->[1]                  while (not ($self->{open_elements}->[-1]->[1]
# Line 5426  sub _tree_construction_main ($) { Line 5806  sub _tree_construction_main ($) {
5806                  !!!cp ('t218');                  !!!cp ('t218');
5807                }                }
5808    
5809                if ($token->{tag_name} eq 'col') {            if ($token->{tag_name} eq 'col') {
5810                  ## Clear back to table context              ## Clear back to table context
5811                  while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
5812                                  & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
5813                    !!!cp ('t219');                !!!cp ('t219');
5814                    ## ISSUE: Can this state be reached?                ## ISSUE: Can this state be reached?
5815                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5816                  }              }
5817                                
5818                  !!!insert-element ('colgroup',, $token);              !!!insert-element ('colgroup',, $token);
5819                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;              $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5820                  ## reprocess              ## reprocess
5821                  !!!ack-later;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5822                  next B;              !!!ack-later;
5823                } elsif ({              next B;
5824                          caption => 1,            } elsif ({
5825                          colgroup => 1,                      caption => 1,
5826                          tbody => 1, tfoot => 1, thead => 1,                      colgroup => 1,
5827                         }->{$token->{tag_name}}) {                      tbody => 1, tfoot => 1, thead => 1,
5828                  ## Clear back to table context                     }->{$token->{tag_name}}) {
5829                ## Clear back to table context
5830                  while (not ($self->{open_elements}->[-1]->[1]                  while (not ($self->{open_elements}->[-1]->[1]
5831                                  & TABLE_SCOPING_EL)) {                                  & TABLE_SCOPING_EL)) {
5832                    !!!cp ('t220');                    !!!cp ('t220');
# Line 5453  sub _tree_construction_main ($) { Line 5834  sub _tree_construction_main ($) {
5834                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5835                  }                  }
5836                                    
5837                  push @$active_formatting_elements, ['#marker', '']              push @$active_formatting_elements, ['#marker', '']
5838                      if $token->{tag_name} eq 'caption';                  if $token->{tag_name} eq 'caption';
5839                                    
5840                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5841                  $self->{insertion_mode} = {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5842                                             caption => IN_CAPTION_IM,              $self->{insertion_mode} = {
5843                                             colgroup => IN_COLUMN_GROUP_IM,                                         caption => IN_CAPTION_IM,
5844                                             tbody => IN_TABLE_BODY_IM,                                         colgroup => IN_COLUMN_GROUP_IM,
5845                                             tfoot => IN_TABLE_BODY_IM,                                         tbody => IN_TABLE_BODY_IM,
5846                                             thead => IN_TABLE_BODY_IM,                                         tfoot => IN_TABLE_BODY_IM,
5847                                            }->{$token->{tag_name}};                                         thead => IN_TABLE_BODY_IM,
5848                  !!!next-token;                                        }->{$token->{tag_name}};
5849                  !!!nack ('t220.1');              !!!next-token;
5850                  next B;              !!!nack ('t220.1');
5851                } else {              next B;
5852                  die "$0: in table: <>: $token->{tag_name}";            } else {
5853                }              die "$0: in table: <>: $token->{tag_name}";
5854              }
5855              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5856                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
5857                                text => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
# Line 5532  sub _tree_construction_main ($) { Line 5914  sub _tree_construction_main ($) {
5914              !!!cp ('t227.8');              !!!cp ('t227.8');
5915              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
5916              $parse_rcdata->(CDATA_CONTENT_MODEL);              $parse_rcdata->(CDATA_CONTENT_MODEL);
5917                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5918              next B;              next B;
5919            } else {            } else {
5920              !!!cp ('t227.7');              !!!cp ('t227.7');
# Line 5542  sub _tree_construction_main ($) { Line 5925  sub _tree_construction_main ($) {
5925              !!!cp ('t227.6');              !!!cp ('t227.6');
5926              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
5927              $script_start_tag->();              $script_start_tag->();
5928                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5929              next B;              next B;
5930            } else {            } else {
5931              !!!cp ('t227.5');              !!!cp ('t227.5');
# Line 5557  sub _tree_construction_main ($) { Line 5941  sub _tree_construction_main ($) {
5941                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
5942    
5943                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5944                    $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5945    
5946                  ## TODO: form element pointer                  ## TODO: form element pointer
5947    
# Line 5886  sub _tree_construction_main ($) { Line 6271  sub _tree_construction_main ($) {
6271        }        }
6272      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6273            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6274              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6275                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6276                unless (length $token->{data}) {                unless (length $token->{data}) {
6277                  !!!cp ('t260');                  !!!cp ('t260');
# Line 6227  sub _tree_construction_main ($) { Line 6612  sub _tree_construction_main ($) {
6612        }        }
6613      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6614        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6615          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6616            my $data = $1;            my $data = $1;
6617            ## As if in body            ## As if in body
6618            $reconstruct_active_formatting_elements->($insert_to_current);            $reconstruct_active_formatting_elements->($insert_to_current);
# Line 6244  sub _tree_construction_main ($) { Line 6629  sub _tree_construction_main ($) {
6629          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6630            !!!cp ('t301');            !!!cp ('t301');
6631            !!!parse-error (type => 'after html:#text', token => $token);            !!!parse-error (type => 'after html:#text', token => $token);
6632              #
           ## Reprocess in the "after body" insertion mode.  
6633          } else {          } else {
6634            !!!cp ('t302');            !!!cp ('t302');
6635              ## "after body" insertion mode
6636              !!!parse-error (type => 'after body:#text', token => $token);
6637              #
6638          }          }
           
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:#text', token => $token);  
6639    
6640          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6641          ## reprocess          ## reprocess
# Line 6261  sub _tree_construction_main ($) { Line 6645  sub _tree_construction_main ($) {
6645            !!!cp ('t303');            !!!cp ('t303');
6646            !!!parse-error (type => 'after html',            !!!parse-error (type => 'after html',
6647                            text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
6648                        #
           ## Reprocess in the "after body" insertion mode.  
6649          } else {          } else {
6650            !!!cp ('t304');            !!!cp ('t304');
6651              ## "after body" insertion mode
6652              !!!parse-error (type => 'after body',
6653                              text => $token->{tag_name}, token => $token);
6654              #
6655          }          }
6656    
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body',  
                         text => $token->{tag_name}, token => $token);  
   
6657          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6658          !!!ack-later;          !!!ack-later;
6659          ## reprocess          ## reprocess
# Line 6281  sub _tree_construction_main ($) { Line 6664  sub _tree_construction_main ($) {
6664            !!!parse-error (type => 'after html:/',            !!!parse-error (type => 'after html:/',
6665                            text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
6666                        
6667            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6668            ## Reprocess in the "after body" insertion mode.            ## Reprocess.
6669              next B;
6670          } else {          } else {
6671            !!!cp ('t306');            !!!cp ('t306');
6672          }          }
# Line 6320  sub _tree_construction_main ($) { Line 6704  sub _tree_construction_main ($) {
6704        }        }
6705      } elsif ($self->{insertion_mode} & FRAME_IMS) {      } elsif ($self->{insertion_mode} & FRAME_IMS) {
6706        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6707          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6708            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6709                        
6710            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 6330  sub _tree_construction_main ($) { Line 6714  sub _tree_construction_main ($) {
6714            }            }
6715          }          }
6716                    
6717          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6718            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6719              !!!cp ('t311');              !!!cp ('t311');
6720              !!!parse-error (type => 'in frameset:#text', token => $token);              !!!parse-error (type => 'in frameset:#text', token => $token);
# Line 6459  sub _tree_construction_main ($) { Line 6843  sub _tree_construction_main ($) {
6843        } else {        } else {
6844          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6845        }        }
   
       ## ISSUE: An issue in spec here  
6846      } else {      } else {
6847        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
6848      }      }
# Line 6478  sub _tree_construction_main ($) { Line 6860  sub _tree_construction_main ($) {
6860          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
6861          next B;          next B;
6862        } elsif ({        } elsif ({
6863                  base => 1, link => 1,                  base => 1, command => 1, eventsource => 1, link => 1,
6864                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6865          !!!cp ('t334');          !!!cp ('t334');
6866          ## NOTE: This is an "as if in head" code clone, only "-t" differs          ## NOTE: This is an "as if in head" code clone, only "-t" differs
6867          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6868          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}};
6869          !!!ack ('t334.1');          !!!ack ('t334.1');
6870          !!!next-token;          !!!next-token;
6871          next B;          next B;
6872        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
6873          ## NOTE: This is an "as if in head" code clone, only "-t" differs          ## NOTE: This is an "as if in head" code clone, only "-t" differs
6874          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6875          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          my $meta_el = pop @{$self->{open_elements}};
6876    
6877          unless ($self->{confident}) {          unless ($self->{confident}) {
6878            if ($token->{attributes}->{charset}) {            if ($token->{attributes}->{charset}) {
# Line 6507  sub _tree_construction_main ($) { Line 6889  sub _tree_construction_main ($) {
6889            } elsif ($token->{attributes}->{content}) {            } elsif ($token->{attributes}->{content}) {
6890              if ($token->{attributes}->{content}->{value}              if ($token->{attributes}->{content}->{value}
6891                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6892                      [\x09-\x0D\x20]*=                      [\x09\x0A\x0C\x0D\x20]*=
6893                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                      [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6894                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20\x3B]*))/x) {                      ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6895                       /x) {
6896                !!!cp ('t336');                !!!cp ('t336');
6897                ## NOTE: Whether the encoding is supported or not is handled                ## NOTE: Whether the encoding is supported or not is handled
6898                ## in the {change_encoding} callback.                ## in the {change_encoding} callback.
# Line 6568  sub _tree_construction_main ($) { Line 6951  sub _tree_construction_main ($) {
6951          !!!next-token;          !!!next-token;
6952          next B;          next B;
6953        } elsif ({        } elsif ({
6954                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: Start tags for non-phrasing flow content elements
6955                  div => 1, dl => 1, fieldset => 1,  
6956                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  ## NOTE: The normal one
6957                  menu => 1, ol => 1, p => 1, ul => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
6958                    center => 1, datagrid => 1, details => 1, dialog => 1,
6959                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
6960                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
6961                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
6962                    section => 1, ul => 1,
6963                    ## NOTE: As normal, but drops leading newline
6964                  pre => 1, listing => 1,                  pre => 1, listing => 1,
6965                    ## NOTE: As normal, but interacts with the form element pointer
6966                  form => 1,                  form => 1,
6967                    
6968                  table => 1,                  table => 1,
6969                  hr => 1,                  hr => 1,
6970                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
# Line 6640  sub _tree_construction_main ($) { Line 7031  sub _tree_construction_main ($) {
7031            !!!next-token;            !!!next-token;
7032          }          }
7033          next B;          next B;
7034        } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {        } elsif ($token->{tag_name} eq 'li') {
7035          ## has a p element in scope          ## NOTE: As normal, but imply </li> when there's another <li> ...
7036    
7037            ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
7038              ## Interpreted as <li><foo/></li><li/> (non-conforming)
7039              ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
7040              ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
7041              ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
7042              ## object (Fx)
7043              ## Generate non-tree (non-conforming)
7044              ## basefont (IE7 (where basefont is non-void)), center (IE),
7045              ## form (IE), hn (IE)
7046            ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7047              ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7048              ## div (Fx, S)
7049    
7050            my $non_optional;
7051            my $i = -1;
7052    
7053            ## 1.
7054            for my $node (reverse @{$self->{open_elements}}) {
7055              if ($node->[1] & LI_EL) {
7056                ## 2. (a) As if </li>
7057                {
7058                  ## If no </li> - not applied
7059                  #
7060    
7061                  ## Otherwise
7062    
7063                  ## 1. generate implied end tags, except for </li>
7064                  #
7065    
7066                  ## 2. If current node != "li", parse error
7067                  if ($non_optional) {
7068                    !!!parse-error (type => 'not closed',
7069                                    text => $non_optional->[0]->manakai_local_name,
7070                                    token => $token);
7071                    !!!cp ('t355');
7072                  } else {
7073                    !!!cp ('t356');
7074                  }
7075    
7076                  ## 3. Pop
7077                  splice @{$self->{open_elements}}, $i;
7078                }
7079    
7080                last; ## 2. (b) goto 5.
7081              } elsif (
7082                       ## NOTE: not "formatting" and not "phrasing"
7083                       ($node->[1] & SPECIAL_EL or
7084                        $node->[1] & SCOPING_EL) and
7085                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7086    
7087                       (not $node->[1] & ADDRESS_EL) &
7088                       (not $node->[1] & DIV_EL) &
7089                       (not $node->[1] & P_EL)) {
7090                ## 3.
7091                !!!cp ('t357');
7092                last; ## goto 5.
7093              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7094                !!!cp ('t358');
7095                #
7096              } else {
7097                !!!cp ('t359');
7098                $non_optional ||= $node;
7099                #
7100              }
7101              ## 4.
7102              ## goto 2.
7103              $i--;
7104            }
7105    
7106            ## 5. (a) has a |p| element in scope
7107          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7108            if ($_->[1] & P_EL) {            if ($_->[1] & P_EL) {
7109              !!!cp ('t353');              !!!cp ('t353');
7110    
7111                ## NOTE: |<p><li>|, for example.
7112    
7113              !!!back-token; # <x>              !!!back-token; # <x>
7114              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7115                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
# Line 6654  sub _tree_construction_main ($) { Line 7119  sub _tree_construction_main ($) {
7119              last INSCOPE;              last INSCOPE;
7120            }            }
7121          } # INSCOPE          } # INSCOPE
7122              
7123          ## Step 1          ## 5. (b) insert
7124            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7125            !!!nack ('t359.1');
7126            !!!next-token;
7127            next B;
7128          } elsif ($token->{tag_name} eq 'dt' or
7129                   $token->{tag_name} eq 'dd') {
7130            ## NOTE: As normal, but imply </dt> or </dd> when ...
7131    
7132            my $non_optional;
7133          my $i = -1;          my $i = -1;
7134          my $node = $self->{open_elements}->[$i];  
7135          my $li_or_dtdd = {li => {li => 1},          ## 1.
7136                            dt => {dt => 1, dd => 1},          for my $node (reverse @{$self->{open_elements}}) {
7137                            dd => {dt => 1, dd => 1}}->{$token->{tag_name}};            if ($node->[1] & DT_EL or $node->[1] & DD_EL) {
7138          LI: {              ## 2. (a) As if </li>
7139            ## Step 2              {
7140            if ($li_or_dtdd->{$node->[0]->manakai_local_name}) {                ## If no </li> - not applied
7141              if ($i != -1) {                #
7142                !!!cp ('t355');  
7143                !!!parse-error (type => 'not closed',                ## Otherwise
7144                                text => $self->{open_elements}->[-1]->[0]  
7145                                    ->manakai_local_name,                ## 1. generate implied end tags, except for </dt> or </dd>
7146                                token => $token);                #
7147              } else {  
7148                !!!cp ('t356');                ## 2. If current node != "dt"|"dd", parse error
7149                  if ($non_optional) {
7150                    !!!parse-error (type => 'not closed',
7151                                    text => $non_optional->[0]->manakai_local_name,
7152                                    token => $token);
7153                    !!!cp ('t355.1');
7154                  } else {
7155                    !!!cp ('t356.1');
7156                  }
7157    
7158                  ## 3. Pop
7159                  splice @{$self->{open_elements}}, $i;
7160              }              }
7161              splice @{$self->{open_elements}}, $i;  
7162              last LI;              last; ## 2. (b) goto 5.
7163              } elsif (
7164                       ## NOTE: not "formatting" and not "phrasing"
7165                       ($node->[1] & SPECIAL_EL or
7166                        $node->[1] & SCOPING_EL) and
7167                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7168    
7169                       (not $node->[1] & ADDRESS_EL) &
7170                       (not $node->[1] & DIV_EL) &
7171                       (not $node->[1] & P_EL)) {
7172                ## 3.
7173                !!!cp ('t357.1');
7174                last; ## goto 5.
7175              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7176                !!!cp ('t358.1');
7177                #
7178            } else {            } else {
7179              !!!cp ('t357');              !!!cp ('t359.1');
7180            }              $non_optional ||= $node;
7181                          #
           ## Step 3  
           if (not ($node->[1] & FORMATTING_EL) and  
               #not $phrasing_category->{$node->[1]} and  
               ($node->[1] & SPECIAL_EL or  
                $node->[1] & SCOPING_EL) and  
               not ($node->[1] & ADDRESS_EL) and  
               not ($node->[1] & DIV_EL)) {  
             !!!cp ('t358');  
             last LI;  
7182            }            }
7183                        ## 4.
7184            !!!cp ('t359');            ## goto 2.
           ## Step 4  
7185            $i--;            $i--;
7186            $node = $self->{open_elements}->[$i];          }
7187            redo LI;  
7188          } # LI          ## 5. (a) has a |p| element in scope
7189                      INSCOPE: for (reverse @{$self->{open_elements}}) {
7190              if ($_->[1] & P_EL) {
7191                !!!cp ('t353.1');
7192                !!!back-token; # <x>
7193                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7194                          line => $token->{line}, column => $token->{column}};
7195                next B;
7196              } elsif ($_->[1] & SCOPING_EL) {
7197                !!!cp ('t354.1');
7198                last INSCOPE;
7199              }
7200            } # INSCOPE
7201    
7202            ## 5. (b) insert
7203          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7204          !!!nack ('t359.1');          !!!nack ('t359.2');
7205          !!!next-token;          !!!next-token;
7206          next B;          next B;
7207        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
7208            ## NOTE: As normal, but effectively ends parsing
7209    
7210          ## has a p element in scope          ## has a p element in scope
7211          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7212            if ($_->[1] & P_EL) {            if ($_->[1] & P_EL) {
# Line 6939  sub _tree_construction_main ($) { Line 7444  sub _tree_construction_main ($) {
7444          }          }
7445          !!!next-token;          !!!next-token;
7446          next B;          next B;
7447          } elsif ($token->{tag_name} eq 'optgroup' or
7448                   $token->{tag_name} eq 'option') {
7449            ## has an |option| element in scope
7450            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7451              my $node = $self->{open_elements}->[$_];
7452              if ($node->[1] & OPTION_EL) {
7453                !!!cp ('t397.1');
7454                ## NOTE: As if </option>
7455                !!!back-token; # <option> or <optgroup>
7456                $token = {type => END_TAG_TOKEN, tag_name => 'option',
7457                          line => $token->{line}, column => $token->{column}};
7458                next B;
7459              } elsif ($node->[1] & SCOPING_EL) {
7460                !!!cp ('t397.2');
7461                last INSCOPE;
7462              }
7463            } # INSCOPE
7464    
7465            $reconstruct_active_formatting_elements->($insert_to_current);
7466    
7467            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7468    
7469            !!!nack ('t397.3');
7470            !!!next-token;
7471            redo B;
7472        } elsif ($token->{tag_name} eq 'rt' or        } elsif ($token->{tag_name} eq 'rt' or
7473                 $token->{tag_name} eq 'rp') {                 $token->{tag_name} eq 'rp') {
7474          ## has a |ruby| element in scope          ## has a |ruby| element in scope
# Line 6986  sub _tree_construction_main ($) { Line 7516  sub _tree_construction_main ($) {
7516                    
7517          if ($self->{self_closing}) {          if ($self->{self_closing}) {
7518            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
7519            !!!ack ('t398.1');            !!!ack ('t398.6');
7520          } else {          } else {
7521            !!!cp ('t398.2');            !!!cp ('t398.7');
7522            $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;            $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7523            ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion            ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7524            ## mode, "in body" (not "in foreign content") secondary insertion            ## mode, "in body" (not "in foreign content") secondary insertion
# Line 6999  sub _tree_construction_main ($) { Line 7529  sub _tree_construction_main ($) {
7529          next B;          next B;
7530        } elsif ({        } elsif ({
7531                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
7532                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1,
7533                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
7534                  thead => 1, tr => 1,                  thead => 1, tr => 1,
7535                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
# Line 7010  sub _tree_construction_main ($) { Line 7540  sub _tree_construction_main ($) {
7540          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7541          !!!next-token;          !!!next-token;
7542          next B;          next B;
7543                  } elsif ($token->{tag_name} eq 'param' or
7544          ## ISSUE: An issue on HTML5 new elements in the spec.                 $token->{tag_name} eq 'source') {
7545            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7546            pop @{$self->{open_elements}};
7547    
7548            !!!ack ('t398.5');
7549            !!!next-token;
7550            redo B;
7551        } else {        } else {
7552          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'image') {
7553            !!!cp ('t384');            !!!cp ('t384');
# Line 7034  sub _tree_construction_main ($) { Line 7570  sub _tree_construction_main ($) {
7570            !!!nack ('t380.1');            !!!nack ('t380.1');
7571          } elsif ({          } elsif ({
7572                    b => 1, big => 1, em => 1, font => 1, i => 1,                    b => 1, big => 1, em => 1, font => 1, i => 1,
7573                    s => 1, small => 1, strile => 1,                    s => 1, small => 1, strike => 1,
7574                    strong => 1, tt => 1, u => 1,                    strong => 1, tt => 1, u => 1,
7575                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
7576            !!!cp ('t375');            !!!cp ('t375');
# Line 7047  sub _tree_construction_main ($) { Line 7583  sub _tree_construction_main ($) {
7583            !!!ack ('t388.2');            !!!ack ('t388.2');
7584          } elsif ({          } elsif ({
7585                    area => 1, basefont => 1, bgsound => 1, br => 1,                    area => 1, basefont => 1, bgsound => 1, br => 1,
7586                    embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                    embed => 1, img => 1, spacer => 1, wbr => 1,
                   #image => 1,  
7587                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
7588            !!!cp ('t388.1');            !!!cp ('t388.1');
7589            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
# Line 7089  sub _tree_construction_main ($) { Line 7624  sub _tree_construction_main ($) {
7624              }              }
7625            }            }
7626    
7627            !!!parse-error (type => 'start tag not allowed',            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
7628    
7629              !!!parse-error (type => 'unmatched end tag',
7630                            text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
7631            ## NOTE: Ignore the token.            ## NOTE: Ignore the token.
7632            !!!next-token;            !!!next-token;
# Line 7116  sub _tree_construction_main ($) { Line 7653  sub _tree_construction_main ($) {
7653          ## up-to-date, though it has same effect as speced.          ## up-to-date, though it has same effect as speced.
7654          if (@{$self->{open_elements}} > 1 and          if (@{$self->{open_elements}} > 1 and
7655              $self->{open_elements}->[1]->[1] & BODY_EL) {              $self->{open_elements}->[1]->[1] & BODY_EL) {
           ## ISSUE: There is an issue in the spec.  
7656            unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {            unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7657              !!!cp ('t406');              !!!cp ('t406');
7658              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
# Line 7138  sub _tree_construction_main ($) { Line 7674  sub _tree_construction_main ($) {
7674            next B;            next B;
7675          }          }
7676        } elsif ({        } elsif ({
7677                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: End tags for non-phrasing flow content elements
7678                  div => 1, dl => 1, fieldset => 1, listing => 1,  
7679                  menu => 1, ol => 1, pre => 1, ul => 1,                  ## NOTE: The normal ones
7680                    address => 1, article => 1, aside => 1, blockquote => 1,
7681                    center => 1, datagrid => 1, details => 1, dialog => 1,
7682                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
7683                    footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
7684                    ol => 1, pre => 1, section => 1, ul => 1,
7685    
7686                    ## NOTE: As normal, but ... optional tags
7687                  dd => 1, dt => 1, li => 1,                  dd => 1, dt => 1, li => 1,
7688    
7689                  applet => 1, button => 1, marquee => 1, object => 1,                  applet => 1, button => 1, marquee => 1, object => 1,
7690                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7691            ## NOTE: Code for <li> start tags includes "as if </li>" code.
7692            ## Code for <dt> or <dd> start tags includes "as if </dt> or
7693            ## </dd>" code.
7694    
7695          ## has an element in scope          ## has an element in scope
7696          my $i;          my $i;
7697          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 7170  sub _tree_construction_main ($) { Line 7718  sub _tree_construction_main ($) {
7718                    dd => ($token->{tag_name} ne 'dd'),                    dd => ($token->{tag_name} ne 'dd'),
7719                    dt => ($token->{tag_name} ne 'dt'),                    dt => ($token->{tag_name} ne 'dt'),
7720                    li => ($token->{tag_name} ne 'li'),                    li => ($token->{tag_name} ne 'li'),
7721                      option => 1,
7722                      optgroup => 1,
7723                    p => 1,                    p => 1,
7724                    rt => 1,                    rt => 1,
7725                    rp => 1,                    rp => 1,
# Line 7202  sub _tree_construction_main ($) { Line 7752  sub _tree_construction_main ($) {
7752          !!!next-token;          !!!next-token;
7753          next B;          next B;
7754        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
7755            ## NOTE: As normal, but interacts with the form element pointer
7756    
7757          undef $self->{form_element};          undef $self->{form_element};
7758    
7759          ## has an element in scope          ## has an element in scope
# Line 7249  sub _tree_construction_main ($) { Line 7801  sub _tree_construction_main ($) {
7801          !!!next-token;          !!!next-token;
7802          next B;          next B;
7803        } elsif ({        } elsif ({
7804                    ## NOTE: As normal, except acts as a closer for any ...
7805                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7806                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7807          ## has an element in scope          ## has an element in scope
# Line 7294  sub _tree_construction_main ($) { Line 7847  sub _tree_construction_main ($) {
7847          !!!next-token;          !!!next-token;
7848          next B;          next B;
7849        } elsif ($token->{tag_name} eq 'p') {        } elsif ($token->{tag_name} eq 'p') {
7850            ## NOTE: As normal, except </p> implies <p> and ...
7851    
7852          ## has an element in scope          ## has an element in scope
7853            my $non_optional;
7854          my $i;          my $i;
7855          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7856            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
# Line 7305  sub _tree_construction_main ($) { Line 7861  sub _tree_construction_main ($) {
7861            } elsif ($node->[1] & SCOPING_EL) {            } elsif ($node->[1] & SCOPING_EL) {
7862              !!!cp ('t411.1');              !!!cp ('t411.1');
7863              last INSCOPE;              last INSCOPE;
7864              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7865                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
7866                !!!cp ('t411.2');
7867                #
7868              } else {
7869                !!!cp ('t411.3');
7870                $non_optional ||= $node;
7871                #
7872            }            }
7873          } # INSCOPE          } # INSCOPE
7874    
7875          if (defined $i) {          if (defined $i) {
7876            if ($self->{open_elements}->[-1]->[0]->manakai_local_name            ## 1. Generate implied end tags
7877                    ne $token->{tag_name}) {            #
7878    
7879              ## 2. If current node != "p", parse error
7880              if ($non_optional) {
7881              !!!cp ('t412.1');              !!!cp ('t412.1');
7882              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
7883                              text => $self->{open_elements}->[-1]->[0]                              text => $non_optional->[0]->manakai_local_name,
                                 ->manakai_local_name,  
7884                              token => $token);                              token => $token);
7885            } else {            } else {
7886              !!!cp ('t414.1');              !!!cp ('t414.1');
7887            }            }
7888    
7889              ## 3. Pop
7890            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
7891          } else {          } else {
7892            !!!cp ('t413.1');            !!!cp ('t413.1');
# Line 7339  sub _tree_construction_main ($) { Line 7906  sub _tree_construction_main ($) {
7906        } elsif ({        } elsif ({
7907                  a => 1,                  a => 1,
7908                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
7909                  nobr => 1, s => 1, small => 1, strile => 1,                  nobr => 1, s => 1, small => 1, strike => 1,
7910                  strong => 1, tt => 1, u => 1,                  strong => 1, tt => 1, u => 1,
7911                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7912          !!!cp ('t427');          !!!cp ('t427');
# Line 7360  sub _tree_construction_main ($) { Line 7927  sub _tree_construction_main ($) {
7927          ## Ignore the token.          ## Ignore the token.
7928          !!!next-token;          !!!next-token;
7929          next B;          next B;
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                 area => 1, basefont => 1, bgsound => 1,  
                 embed => 1, hr => 1, iframe => 1, image => 1,  
                 img => 1, input => 1, isindex => 1, noembed => 1,  
                 noframes => 1, param => 1, select => 1, spacer => 1,  
                 table => 1, textarea => 1, wbr => 1,  
                 noscript => 0, ## TODO: if scripting is enabled  
                }->{$token->{tag_name}}) {  
         !!!cp ('t429');  
         !!!parse-error (type => 'unmatched end tag',  
                         text => $token->{tag_name}, token => $token);  
         ## Ignore the token  
         !!!next-token;  
         next B;  
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
7930        } else {        } else {
7931            if ($token->{tag_name} eq 'sarcasm') {
7932              sleep 0.001; # take a deep breath
7933            }
7934    
7935          ## Step 1          ## Step 1
7936          my $node_i = -1;          my $node_i = -1;
7937          my $node = $self->{open_elements}->[$node_i];          my $node = $self->{open_elements}->[$node_i];
7938    
7939          ## Step 2          ## Step 2
7940          S2: {          S2: {
7941            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {            my $node_tag_name = $node->[0]->manakai_local_name;
7942              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
7943              if ($node_tag_name eq $token->{tag_name}) {
7944              ## Step 1              ## Step 1
7945              ## generate implied end tags              ## generate implied end tags
7946              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
# Line 7401  sub _tree_construction_main ($) { Line 7953  sub _tree_construction_main ($) {
7953              }              }
7954                    
7955              ## Step 2              ## Step 2
7956              if ($self->{open_elements}->[-1]->[0]->manakai_local_name              my $current_tag_name
7957                      ne $token->{tag_name}) {                  = $self->{open_elements}->[-1]->[0]->manakai_local_name;
7958                $current_tag_name =~ tr/A-Z/a-z/;
7959                if ($current_tag_name ne $token->{tag_name}) {
7960                !!!cp ('t431');                !!!cp ('t431');
7961                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
7962                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
# Line 7430  sub _tree_construction_main ($) { Line 7984  sub _tree_construction_main ($) {
7984                ## Ignore the token                ## Ignore the token
7985                !!!next-token;                !!!next-token;
7986                last S2;                last S2;
             }  
7987    
7988                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7989                  ## 9.27, "a" is a child of <dd> (conforming).  In
7990                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7991                  ## "a" is a child of both <body> and <dd>.
7992                }
7993                
7994              !!!cp ('t434');              !!!cp ('t434');
7995            }            }
7996                        
# Line 7472  sub _tree_construction_main ($) { Line 8031  sub _tree_construction_main ($) {
8031    ## TODO: script stuffs    ## TODO: script stuffs
8032  } # _tree_construct_main  } # _tree_construct_main
8033    
8034  sub set_inner_html ($$$;$) {  sub set_inner_html ($$$$;$) {
8035    my $class = shift;    my $class = shift;
8036    my $node = shift;    my $node = shift;
8037    my $s = \$_[0];    #my $s = \$_[0];
8038    my $onerror = $_[1];    my $onerror = $_[1];
8039    my $get_wrapper = $_[2] || sub ($) { return $_[0] };    my $get_wrapper = $_[2] || sub ($) { return $_[0] };
8040    
# Line 7496  sub set_inner_html ($$$;$) { Line 8055  sub set_inner_html ($$$;$) {
8055      }      }
8056    
8057      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
8058      $class->parse_char_string ($$s => $node, $onerror, $get_wrapper);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
8059    } elsif ($nt == 1) {    } elsif ($nt == 1) {
8060      ## TODO: If non-html element      ## TODO: If non-html element
8061    
# Line 7515  sub set_inner_html ($$$;$) { Line 8074  sub set_inner_html ($$$;$) {
8074      my $i = 0;      my $i = 0;
8075      $p->{line_prev} = $p->{line} = 1;      $p->{line_prev} = $p->{line} = 1;
8076      $p->{column_prev} = $p->{column} = 0;      $p->{column_prev} = $p->{column} = 0;
8077      $p->{set_next_char} = sub {      require Whatpm::Charset::DecodeHandle;
8078        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
8079        $input = $get_wrapper->($input);
8080        $p->{set_nc} = sub {
8081        my $self = shift;        my $self = shift;
8082    
8083        pop @{$self->{prev_char}};        my $char = '';
8084        unshift @{$self->{prev_char}}, $self->{next_char};        if (defined $self->{next_nc}) {
8085            $char = $self->{next_nc};
8086        $self->{next_char} = -1 and return if $i >= length $$s;          delete $self->{next_nc};
8087        $self->{next_char} = ord substr $$s, $i++, 1;          $self->{nc} = ord $char;
8088          } else {
8089            $self->{char_buffer} = '';
8090            $self->{char_buffer_pos} = 0;
8091            
8092            my $count = $input->manakai_read_until
8093                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
8094                 $self->{char_buffer_pos});
8095            if ($count) {
8096              $self->{line_prev} = $self->{line};
8097              $self->{column_prev} = $self->{column};
8098              $self->{column}++;
8099              $self->{nc}
8100                  = ord substr ($self->{char_buffer},
8101                                $self->{char_buffer_pos}++, 1);
8102              return;
8103            }
8104            
8105            if ($input->read ($char, 1)) {
8106              $self->{nc} = ord $char;
8107            } else {
8108              $self->{nc} = -1;
8109              return;
8110            }
8111          }
8112    
8113        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8114        $p->{column}++;        $p->{column}++;
8115    
8116        if ($self->{next_char} == 0x000A) { # LF        if ($self->{nc} == 0x000A) { # LF
8117          $p->{line}++;          $p->{line}++;
8118          $p->{column} = 0;          $p->{column} = 0;
8119          !!!cp ('i1');          !!!cp ('i1');
8120        } elsif ($self->{next_char} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
8121          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
8122          $self->{next_char} = 0x000A; # LF # MUST          my $next = '';
8123            if ($input->read ($next, 1) and $next ne "\x0A") {
8124              $self->{next_nc} = $next;
8125            }
8126            $self->{nc} = 0x000A; # LF # MUST
8127          $p->{line}++;          $p->{line}++;
8128          $p->{column} = 0;          $p->{column} = 0;
8129          !!!cp ('i2');          !!!cp ('i2');
8130        } 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  
8131          !!!cp ('i4');          !!!cp ('i4');
8132          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
8133          $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');  
         if ($self->{next_char} < 0x10000) {  
           !!!parse-error (type => 'control char',  
                           text => (sprintf 'U+%04X', $self->{next_char}));  
         } else {  
           !!!parse-error (type => 'control char',  
                           text => (sprintf 'U-%08X', $self->{next_char}));  
         }  
8134        }        }
8135      };      };
8136      $p->{prev_char} = [-1, -1, -1];  
8137      $p->{next_char} = -1;      $p->{read_until} = sub {
8138              #my ($scalar, $specials_range, $offset) = @_;
8139          return 0 if defined $p->{next_nc};
8140    
8141          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
8142          my $offset = $_[2] || 0;
8143          
8144          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
8145            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
8146            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
8147              substr ($_[0], $offset)
8148                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
8149              my $count = $+[0] - $-[0];
8150              if ($count) {
8151                $p->{column} += $count;
8152                $p->{char_buffer_pos} += $count;
8153                $p->{line_prev} = $p->{line};
8154                $p->{column_prev} = $p->{column} - 1;
8155                $p->{nc} = -1;
8156              }
8157              return $count;
8158            } else {
8159              return 0;
8160            }
8161          } else {
8162            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
8163            if ($count) {
8164              $p->{column} += $count;
8165              $p->{column_prev} += $count;
8166              $p->{nc} = -1;
8167            }
8168            return $count;
8169          }
8170        }; # $p->{read_until}
8171    
8172      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
8173        my (%opt) = @_;        my (%opt) = @_;
8174        my $line = $opt{line};        my $line = $opt{line};
# Line 7591  sub set_inner_html ($$$;$) { Line 8183  sub set_inner_html ($$$;$) {
8183        $ponerror->(line => $p->{line}, column => $p->{column}, @_);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
8184      };      };
8185            
8186        my $char_onerror = sub {
8187          my (undef, $type, %opt) = @_;
8188          $ponerror->(layer => 'encode',
8189                      line => $p->{line}, column => $p->{column} + 1,
8190                      %opt, type => $type);
8191        }; # $char_onerror
8192        $input->onerror ($char_onerror);
8193    
8194      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
8195      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
8196    
# Line 7626  sub set_inner_html ($$$;$) { Line 8226  sub set_inner_html ($$$;$) {
8226      push @{$p->{open_elements}}, [$root, $el_category->{html}];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
8227    
8228      undef $p->{head_element};      undef $p->{head_element};
8229        undef $p->{head_element_inserted};
8230    
8231      ## Step 6 # MUST      ## Step 6 # MUST
8232      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;

Legend:
Removed from v.1.162  
changed lines
  Added in v.1.202

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24