/[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.142 by wakaba, Sat May 24 10:32:29 2008 UTC revision 1.201 by wakaba, Sat Oct 4 12:20:35 2008 UTC
# Line 3  use strict; Line 3  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4  use Error qw(:try);  use Error qw(:try);
5    
6    ## NOTE: This module don't check all HTML5 parse errors; character
7    ## encoding related parse errors are expected to be handled by relevant
8    ## modules.
9    ## Parse errors for control characters that are not allowed in HTML5
10    ## documents, for surrogate code points, and for noncharacter code
11    ## points, as well as U+FFFD substitions for characters whose code points
12    ## is higher than U+10FFFF may be detected by combining the parser with
13    ## the checker implemented by Whatpm::Charset::UnicodeChecker (for its
14    ## usage example, see |t/HTML-tree.t| in the Whatpm package or the
15    ## WebHACC::Language::HTML module in the WebHACC package).
16    
17  ## ISSUE:  ## ISSUE:
18  ## var doc = implementation.createDocument (null, null, null);  ## var doc = implementation.createDocument (null, null, null);
19  ## doc.write ('');  ## doc.write ('');
20  ## alert (doc.compatMode);  ## alert (doc.compatMode);
21    
 ## TODO: 1252 parse error (revision 1264)  
 ## TODO: 8859-11 = 874 (revision 1271)  
   
22  require IO::Handle;  require IO::Handle;
23    
24  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
# Line 48  sub MISC_SPECIAL_EL () { 0b1000000000000 Line 56  sub MISC_SPECIAL_EL () { 0b1000000000000
56  sub FOREIGN_EL () { 0b10000000000000000000000000 }  sub FOREIGN_EL () { 0b10000000000000000000000000 }
57  sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }  sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }
58  sub MML_AXML_EL () { 0b1000000000000000000000000000 }  sub MML_AXML_EL () { 0b1000000000000000000000000000 }
59    sub RUBY_EL () { 0b10000000000000000000000000000 }
60    sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }
61    
62  sub TABLE_ROWS_EL () {  sub TABLE_ROWS_EL () {
63    TABLE_EL |    TABLE_EL |
# Line 55  sub TABLE_ROWS_EL () { Line 65  sub TABLE_ROWS_EL () {
65    TABLE_ROW_GROUP_EL    TABLE_ROW_GROUP_EL
66  }  }
67    
68    ## NOTE: Used in "generate implied end tags" algorithm.
69    ## NOTE: There is a code where a modified version of
70    ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
71    ## implementation (search for the algorithm name).
72  sub END_TAG_OPTIONAL_EL () {  sub END_TAG_OPTIONAL_EL () {
73    DD_EL |    DD_EL |
74    DT_EL |    DT_EL |
75    LI_EL |    LI_EL |
76    P_EL    OPTION_EL |
77      OPTGROUP_EL |
78      P_EL |
79      RUBY_COMPONENT_EL
80  }  }
81    
82    ## NOTE: Used in </body> and EOF algorithms.
83  sub ALL_END_TAG_OPTIONAL_EL () {  sub ALL_END_TAG_OPTIONAL_EL () {
84    END_TAG_OPTIONAL_EL |    DD_EL |
85      DT_EL |
86      LI_EL |
87      P_EL |
88    
89      ## ISSUE: option, optgroup, rt, rp?
90    
91    BODY_EL |    BODY_EL |
92    HTML_EL |    HTML_EL |
93    TABLE_CELL_EL |    TABLE_CELL_EL |
# Line 99  sub SPECIAL_EL () { Line 123  sub SPECIAL_EL () {
123    ADDRESS_EL |    ADDRESS_EL |
124    BODY_EL |    BODY_EL |
125    DIV_EL |    DIV_EL |
126    END_TAG_OPTIONAL_EL |  
127      DD_EL |
128      DT_EL |
129      LI_EL |
130      P_EL |
131    
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 116  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 129  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 148  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 161  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 173  my $el_category = { Line 212  my $el_category = {
212    param => MISC_SPECIAL_EL,    param => MISC_SPECIAL_EL,
213    plaintext => MISC_SPECIAL_EL,    plaintext => MISC_SPECIAL_EL,
214    pre => MISC_SPECIAL_EL,    pre => MISC_SPECIAL_EL,
215      rp => RUBY_COMPONENT_EL,
216      rt => RUBY_COMPONENT_EL,
217      ruby => RUBY_EL,
218    s => FORMATTING_EL,    s => FORMATTING_EL,
219    script => MISC_SPECIAL_EL,    script => MISC_SPECIAL_EL,
220    select => SELECT_EL,    select => SELECT_EL,
221      section => MISC_SPECIAL_EL,
222    small => FORMATTING_EL,    small => FORMATTING_EL,
223    spacer => MISC_SPECIAL_EL,    spacer => MISC_SPECIAL_EL,
224    strike => FORMATTING_EL,    strike => FORMATTING_EL,
# Line 206  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 214  my $el_category_f = { Line 257  my $el_category_f = {
257  };  };
258    
259  my $svg_attr_name = {  my $svg_attr_name = {
260      attributename => 'attributeName',
261    attributetype => 'attributeType',    attributetype => 'attributeType',
262    basefrequency => 'baseFrequency',    basefrequency => 'baseFrequency',
263    baseprofile => 'baseProfile',    baseprofile => 'baseProfile',
# Line 224  my $svg_attr_name = { Line 268  my $svg_attr_name = {
268    diffuseconstant => 'diffuseConstant',    diffuseconstant => 'diffuseConstant',
269    edgemode => 'edgeMode',    edgemode => 'edgeMode',
270    externalresourcesrequired => 'externalResourcesRequired',    externalresourcesrequired => 'externalResourcesRequired',
   fecolormatrix => 'feColorMatrix',  
   fecomposite => 'feComposite',  
   fegaussianblur => 'feGaussianBlur',  
   femorphology => 'feMorphology',  
   fetile => 'feTile',  
271    filterres => 'filterRes',    filterres => 'filterRes',
272    filterunits => 'filterUnits',    filterunits => 'filterUnits',
273    glyphref => 'glyphRef',    glyphref => 'glyphRef',
# Line 262  my $svg_attr_name = { Line 301  my $svg_attr_name = {
301    repeatcount => 'repeatCount',    repeatcount => 'repeatCount',
302    repeatdur => 'repeatDur',    repeatdur => 'repeatDur',
303    requiredextensions => 'requiredExtensions',    requiredextensions => 'requiredExtensions',
304      requiredfeatures => 'requiredFeatures',
305    specularconstant => 'specularConstant',    specularconstant => 'specularConstant',
306    specularexponent => 'specularExponent',    specularexponent => 'specularExponent',
307    spreadmethod => 'spreadMethod',    spreadmethod => 'spreadMethod',
# Line 298  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 331  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 340  sub parse_byte_string ($$$$;$) { Line 392  sub parse_byte_string ($$$$;$) {
392    return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);    return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
393  } # parse_byte_string  } # parse_byte_string
394    
395  sub parse_byte_stream ($$$$;$) {  sub parse_byte_stream ($$$$;$$) {
396      # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
397    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
398    my $charset_name = shift;    my $charset_name = shift;
399    my $byte_stream = $_[0];    my $byte_stream = $_[0];
# Line 351  sub parse_byte_stream ($$$$;$) { Line 404  sub parse_byte_stream ($$$$;$) {
404    };    };
405    $self->{parse_error} = $onerror; # updated later by parse_char_string    $self->{parse_error} = $onerror; # updated later by parse_char_string
406    
407      my $get_wrapper = $_[3] || sub ($) {
408        return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
409      };
410    
411    ## HTML5 encoding sniffing algorithm    ## HTML5 encoding sniffing algorithm
412    require Message::Charset::Info;    require Message::Charset::Info;
413    my $charset;    my $charset;
# Line 358  sub parse_byte_stream ($$$$;$) { Line 415  sub parse_byte_stream ($$$$;$) {
415    my ($char_stream, $e_status);    my ($char_stream, $e_status);
416    
417    SNIFFING: {    SNIFFING: {
418        ## NOTE: By setting |allow_fallback| option true when the
419        ## |get_decode_handle| method is invoked, we ignore what the HTML5
420        ## spec requires, i.e. unsupported encoding should be ignored.
421          ## TODO: We should not do this unless the parser is invoked
422          ## in the conformance checking mode, in which this behavior
423          ## would be useful.
424    
425      ## Step 1      ## Step 1
426      if (defined $charset_name) {      if (defined $charset_name) {
427        $charset = Message::Charset::Info->get_by_iana_name ($charset_name);        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
428              ## TODO: Is this ok?  Transfer protocol's parameter should be
429              ## interpreted in its semantics?
430    
       ## 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 371  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 385  sub parse_byte_stream ($$$$;$) { Line 453  sub parse_byte_stream ($$$$;$) {
453    
454      ## Step 3      ## Step 3
455      if ($byte_buffer =~ /^\xFE\xFF/) {      if ($byte_buffer =~ /^\xFE\xFF/) {
456        $charset = Message::Charset::Info->get_by_iana_name ('utf-16be');        $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
457        ($char_stream, $e_status) = $charset->get_decode_handle        ($char_stream, $e_status) = $charset->get_decode_handle
458            ($byte_stream, allow_error_reporting => 1,            ($byte_stream, allow_error_reporting => 1,
459             allow_fallback => 1, byte_buffer => \$byte_buffer);             allow_fallback => 1, byte_buffer => \$byte_buffer);
460        $self->{confident} = 1;        $self->{confident} = 1;
461        last SNIFFING;        last SNIFFING;
462      } elsif ($byte_buffer =~ /^\xFF\xFE/) {      } elsif ($byte_buffer =~ /^\xFF\xFE/) {
463        $charset = Message::Charset::Info->get_by_iana_name ('utf-16le');        $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
464        ($char_stream, $e_status) = $charset->get_decode_handle        ($char_stream, $e_status) = $charset->get_decode_handle
465            ($byte_stream, allow_error_reporting => 1,            ($byte_stream, allow_error_reporting => 1,
466             allow_fallback => 1, byte_buffer => \$byte_buffer);             allow_fallback => 1, byte_buffer => \$byte_buffer);
467        $self->{confident} = 1;        $self->{confident} = 1;
468        last SNIFFING;        last SNIFFING;
469      } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {      } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
470        $charset = Message::Charset::Info->get_by_iana_name ('utf-8');        $charset = Message::Charset::Info->get_by_html_name ('utf-8');
471        ($char_stream, $e_status) = $charset->get_decode_handle        ($char_stream, $e_status) = $charset->get_decode_handle
472            ($byte_stream, allow_error_reporting => 1,            ($byte_stream, allow_error_reporting => 1,
473             allow_fallback => 1, byte_buffer => \$byte_buffer);             allow_fallback => 1, byte_buffer => \$byte_buffer);
# Line 418  sub parse_byte_stream ($$$$;$) { Line 486  sub parse_byte_stream ($$$$;$) {
486      $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string      $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
487          ($byte_buffer);          ($byte_buffer);
488      if (defined $charset_name) {      if (defined $charset_name) {
489        $charset = Message::Charset::Info->get_by_iana_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 429  sub parse_byte_stream ($$$$;$) { Line 496  sub parse_byte_stream ($$$$;$) {
496             allow_fallback => 1, byte_buffer => \$byte_buffer);             allow_fallback => 1, byte_buffer => \$byte_buffer);
497        if ($char_stream) {        if ($char_stream) {
498          $buffer->{buffer} = $byte_buffer;          $buffer->{buffer} = $byte_buffer;
499          !!!parse-error (type => 'sniffing:chardet', ## TODO: type name          !!!parse-error (type => 'sniffing:chardet',
500                          value => $charset_name,                          text => $charset_name,
501                          level => $self->{info_level},                          level => $self->{level}->{info},
502                            layer => 'encode',
503                          line => 1, column => 1);                          line => 1, column => 1);
504          $self->{confident} = 0;          $self->{confident} = 0;
505          last SNIFFING;          last SNIFFING;
# Line 440  sub parse_byte_stream ($$$$;$) { Line 508  sub parse_byte_stream ($$$$;$) {
508    
509      ## Step 7: default      ## Step 7: default
510      ## TODO: Make this configurable.      ## TODO: Make this configurable.
511      $charset = Message::Charset::Info->get_by_iana_name ('windows-1252');      $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
512          ## NOTE: We choose |windows-1252| here, since |utf-8| should be          ## NOTE: We choose |windows-1252| here, since |utf-8| should be
513          ## detectable in the step 6.          ## detectable in the step 6.
514      require Whatpm::Charset::DecodeHandle;      require Whatpm::Charset::DecodeHandle;
# Line 452  sub parse_byte_stream ($$$$;$) { Line 520  sub parse_byte_stream ($$$$;$) {
520                                         allow_fallback => 1,                                         allow_fallback => 1,
521                                         byte_buffer => \$byte_buffer);                                         byte_buffer => \$byte_buffer);
522      $buffer->{buffer} = $byte_buffer;      $buffer->{buffer} = $byte_buffer;
523      !!!parse-error (type => 'sniffing:default', ## TODO: type name      !!!parse-error (type => 'sniffing:default',
524                      value => 'windows-1252',                      text => 'windows-1252',
525                      level => $self->{info_level},                      level => $self->{level}->{info},
526                      line => 1, column => 1);                      line => 1, column => 1,
527                        layer => 'encode');
528      $self->{confident} = 0;      $self->{confident} = 0;
529    } # SNIFFING    } # SNIFFING
530    
   $self->{input_encoding} = $charset->get_iana_name;  
531    if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {    if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
532      !!!parse-error (type => 'chardecode:fallback', ## TODO: type name      $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
533                      value => $self->{input_encoding},      !!!parse-error (type => 'chardecode:fallback',
534                      level => $self->{unsupported_level},                      #text => $self->{input_encoding},
535                      line => 1, column => 1);                      level => $self->{level}->{uncertain},
536                        line => 1, column => 1,
537                        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      !!!parse-error (type => 'chardecode:no error', ## TODO: type name      $self->{input_encoding} = $charset->get_iana_name;
541                      value => $self->{input_encoding},      !!!parse-error (type => 'chardecode:no error',
542                      level => $self->{unsupported_level},                      text => $self->{input_encoding},
543                      line => 1, column => 1);                      level => $self->{level}->{uncertain},
544                        line => 1, column => 1,
545                        layer => 'encode');
546      } else {
547        $self->{input_encoding} = $charset->get_iana_name;
548    }    }
549    
550    $self->{change_encoding} = sub {    $self->{change_encoding} = sub {
# Line 478  sub parse_byte_stream ($$$$;$) { Line 552  sub parse_byte_stream ($$$$;$) {
552      $charset_name = shift;      $charset_name = shift;
553      my $token = shift;      my $token = shift;
554    
555      $charset = Message::Charset::Info->get_by_iana_name ($charset_name);      $charset = Message::Charset::Info->get_by_html_name ($charset_name);
556      ($char_stream, $e_status) = $charset->get_decode_handle      ($char_stream, $e_status) = $charset->get_decode_handle
557          ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,          ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
558           byte_buffer => \ $buffer->{buffer});           byte_buffer => \ $buffer->{buffer});
# Line 487  sub parse_byte_stream ($$$$;$) { Line 561  sub parse_byte_stream ($$$$;$) {
561        ## "Change the encoding" algorithm:        ## "Change the encoding" algorithm:
562    
563        ## Step 1            ## Step 1    
564        if ($charset->{iana_names}->{'utf-16'}) { ## ISSUE: UTF-16BE -> UTF-8? UTF-16LE -> UTF-8?        if ($charset->{category} &
565          $charset = Message::Charset::Info->get_by_iana_name ('utf-8');            Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
566            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
567          ($char_stream, $e_status) = $charset->get_decode_handle          ($char_stream, $e_status) = $charset->get_decode_handle
568              ($byte_stream,              ($byte_stream,
569               byte_buffer => \ $buffer->{buffer});               byte_buffer => \ $buffer->{buffer});
# Line 498  sub parse_byte_stream ($$$$;$) { Line 573  sub parse_byte_stream ($$$$;$) {
573        ## Step 2        ## Step 2
574        if (defined $self->{input_encoding} and        if (defined $self->{input_encoding} and
575            $self->{input_encoding} eq $charset_name) {            $self->{input_encoding} eq $charset_name) {
576          !!!parse-error (type => 'charset label:matching', ## TODO: type          !!!parse-error (type => 'charset label:matching',
577                          value => $charset_name,                          text => $charset_name,
578                          level => $self->{info_level});                          level => $self->{level}->{info});
579          $self->{confident} = 1;          $self->{confident} = 1;
580          return;          return;
581        }        }
582    
583        !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}.        !!!parse-error (type => 'charset label detected',
584            ':'.$charset_name, level => 'w', token => $token);                        text => $self->{input_encoding},
585                          value => $charset_name,
586                          level => $self->{level}->{warn},
587                          token => $token);
588                
589        ## Step 3        ## Step 3
590        # if (can) {        # if (can) {
# Line 522  sub parse_byte_stream ($$$$;$) { Line 600  sub parse_byte_stream ($$$$;$) {
600    
601    my $char_onerror = sub {    my $char_onerror = sub {
602      my (undef, $type, %opt) = @_;      my (undef, $type, %opt) = @_;
603      !!!parse-error (%opt, type => $type,      !!!parse-error (layer => 'encode',
604                      line => $self->{line}, column => $self->{column} + 1);                      line => $self->{line}, column => $self->{column} + 1,
605                        %opt, type => $type);
606      if ($opt{octets}) {      if ($opt{octets}) {
607        ${$opt{octets}} = "\x{FFFD}"; # relacement character        ${$opt{octets}} = "\x{FFFD}"; # relacement character
608      }      }
609    };    };
   $char_stream->onerror ($char_onerror);  
610    
611    my @args = @_; shift @args; # $s    my $wrapped_char_stream = $get_wrapper->($char_stream);
612      $wrapped_char_stream->onerror ($char_onerror);
613    
614      my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
615    my $return;    my $return;
616    try {    try {
617      $return = $self->parse_char_stream ($char_stream, @args);        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
618    } catch Whatpm::HTML::RestartParser with {    } catch Whatpm::HTML::RestartParser with {
619      ## NOTE: Invoked after {change_encoding}.      ## NOTE: Invoked after {change_encoding}.
620    
     $self->{input_encoding} = $charset->get_iana_name;  
621      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
622        !!!parse-error (type => 'chardecode:fallback', ## TODO: type name        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
623                        value => $self->{input_encoding},        !!!parse-error (type => 'chardecode:fallback',
624                        level => $self->{unsupported_level},                        level => $self->{level}->{uncertain},
625                        line => 1, column => 1);                        #text => $self->{input_encoding},
626                          line => 1, column => 1,
627                          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        !!!parse-error (type => 'chardecode:no error', ## TODO: type name        $self->{input_encoding} = $charset->get_iana_name;
631                        value => $self->{input_encoding},        !!!parse-error (type => 'chardecode:no error',
632                        level => $self->{unsupported_level},                        text => $self->{input_encoding},
633                        line => 1, column => 1);                        level => $self->{level}->{uncertain},
634                          line => 1, column => 1,
635                          layer => 'encode');
636        } else {
637          $self->{input_encoding} = $charset->get_iana_name;
638      }      }
639      $self->{confident} = 1;      $self->{confident} = 1;
640      $char_stream->onerror ($char_onerror);  
641      $return = $self->parse_char_stream ($char_stream, @args);      $wrapped_char_stream = $get_wrapper->($char_stream);
642        $wrapped_char_stream->onerror ($char_onerror);
643    
644        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
645    };    };
646    return $return;    return $return;
647  } # parse_byte_stream  } # parse_byte_stream
# Line 566  sub parse_byte_stream ($$$$;$) { Line 655  sub parse_byte_stream ($$$$;$) {
655  ## such as |parse_byte_string| in this module, must ensure that it does  ## such as |parse_byte_string| in this module, must ensure that it does
656  ## strip the BOM and never strip any ZWNBSP.  ## strip the BOM and never strip any ZWNBSP.
657    
658  sub parse_char_string ($$$;$) {  sub parse_char_string ($$$;$$) {
659      #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      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
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;  *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 586  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
     } elsif ($self->{next_char} <= 0x0008 or  
              (0x000E <= $self->{next_char} and $self->{next_char} <= 0x001F) or  
              (0x007F <= $self->{next_char} and $self->{next_char} <= 0x009F) or  
              (0xD800 <= $self->{next_char} and $self->{next_char} <= 0xDFFF) or  
              (0xFDD0 <= $self->{next_char} and $self->{next_char} <= 0xFDDF) or  
              {  
               0xFFFE => 1, 0xFFFF => 1, 0x1FFFE => 1, 0x1FFFF => 1,  
               0x2FFFE => 1, 0x2FFFF => 1, 0x3FFFE => 1, 0x3FFFF => 1,  
               0x4FFFE => 1, 0x4FFFF => 1, 0x5FFFE => 1, 0x5FFFF => 1,  
               0x6FFFE => 1, 0x6FFFF => 1, 0x7FFFE => 1, 0x7FFFF => 1,  
               0x8FFFE => 1, 0x8FFFF => 1, 0x9FFFE => 1, 0x9FFFF => 1,  
               0xAFFFE => 1, 0xAFFFF => 1, 0xBFFFE => 1, 0xBFFFF => 1,  
               0xCFFFE => 1, 0xCFFFF => 1, 0xDFFFE => 1, 0xDFFFF => 1,  
               0xEFFFE => 1, 0xEFFFF => 1, 0xFFFFE => 1, 0xFFFFF => 1,  
               0x10FFFE => 1, 0x10FFFF => 1,  
              }->{$self->{next_char}}) {  
       !!!cp ('j5');  
       !!!parse-error (type => 'control char', level => $self->{must_level});  
 ## TODO: error type documentation  
737      }      }
738    };    };
739    $self->{prev_char} = [-1, -1, -1];  
740    $self->{next_char} = -1;    $self->{read_until} = sub {
741        #my ($scalar, $specials_range, $offset) = @_;
742        return 0 if defined $self->{next_nc};
743    
744        my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
745        my $offset = $_[2] || 0;
746    
747        if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
748          pos ($self->{char_buffer}) = $self->{char_buffer_pos};
749          if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
750            substr ($_[0], $offset)
751                = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
752            my $count = $+[0] - $-[0];
753            if ($count) {
754              $self->{column} += $count;
755              $self->{char_buffer_pos} += $count;
756              $self->{line_prev} = $self->{line};
757              $self->{column_prev} = $self->{column} - 1;
758              $self->{nc} = -1;
759            }
760            return $count;
761          } else {
762            return 0;
763          }
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}
775    
776    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
777      my (%opt) = @_;      my (%opt) = @_;
# Line 664  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 677  sub parse_char_stream ($$$;$) { Line 810  sub parse_char_stream ($$$;$) {
810  sub new ($) {  sub new ($) {
811    my $class = shift;    my $class = shift;
812    my $self = bless {    my $self = bless {
813      must_level => 'm',      level => {must => 'm',
814      should_level => 's',                should => 's',
815      good_level => 'w',                warn => 'w',
816      warn_level => 'w',                info => 'i',
817      info_level => 'i',                uncertain => 'u'},
     unsupported_level => '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 712  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 723  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 746  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 799  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 817  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 829  sub _initialize_tokenizer ($) { Line 984  sub _initialize_tokenizer ($) {
984  ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|  ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
985  ##     while the token is pushed back to the stack.  ##     while the token is pushed back to the stack.
986    
 ## ISSUE: "When a DOCTYPE token is created, its  
 ## <i>self-closing flag</i> must be unset (its other state is that it  
 ## be set), and its attributes list must be empty.": Wrong subject?  
   
987  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
988    
989  ## Before each step, UA MAY check to see if either one of the scripts in  ## Before each step, UA MAY check to see if either one of the scripts in
# Line 841  sub _initialize_tokenizer ($) { Line 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 873  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 912  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 928  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 1034  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 1065  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 1155  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 1170  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 1201  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 1232  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 1326  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 1342  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:'.$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 1419  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 1469  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 1507  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          !!!cp (82);          if ($self->{nc} == 0x0022 or # "
1728          $self->{current_attribute}              $self->{nc} == 0x0027) { # '
1729              = {name => chr ($self->{next_char}),            !!!cp (78);
1730              !!!parse-error (type => 'bad attribute name');
1731            } else {
1732              !!!cp (82);
1733            }
1734            $self->{ca}
1735                = {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 1526  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          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          !!!parse-error (type => 'empty unquoted attribute value');
1765            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 1564  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 1587  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 1634  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 1676  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 1721  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 1744  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 1757  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 1814  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 1841  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 1855  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 1876  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 1893  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 1909  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 => ''};  
   
       BC: {  
         if ($self->{next_char} == 0x003E) { # >  
           !!!cp (124);  
           $self->{state} = DATA_STATE;  
           !!!next-input-character;  
   
           !!!emit ($self->{current_token}); # comment  
2120    
2121            redo A;        ## NOTE: Unlike spec's "bogus comment state", this implementation
2122          } elsif ($self->{next_char} == -1) {        ## consumes characters one-by-one basis.
2123            !!!cp (125);        
2124            $self->{state} = DATA_STATE;        if ($self->{nc} == 0x003E) { # >
2125            ## reconsume          !!!cp (124);
2126            $self->{state} = DATA_STATE;
2127            !!!next-input-character;
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 2212  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 2235  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 PUBLIC literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2877    
2878          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2879          !!!next-input-character;          !!!next-input-character;
2880    
2881          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2882          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2883    
2884          redo A;          redo A;
2885        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2886          !!!cp (209);          !!!cp (209);
2887          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2888    
2889          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2890          ## reconsume          ## reconsume
2891    
2892          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2893          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2894    
2895          redo A;          redo A;
2896        } else {        } else {
2897          !!!cp (210);          !!!cp (210);
2898          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2899              .= chr $self->{next_char};              .= chr $self->{nc};
2900            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2901                                  length $self->{ct}->{sysid});
2902    
2903          ## Stay in the state          ## Stay in the state
2904          !!!next-input-character;          !!!next-input-character;
2905          redo A;          redo A;
2906        }        }
2907      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2908        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2909          !!!cp (211);          !!!cp (211);
2910          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2911          !!!next-input-character;          !!!next-input-character;
2912          redo A;          redo A;
2913        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2914          !!!cp (212);          !!!cp (212);
2915          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2916    
2917          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2918          !!!next-input-character;          !!!next-input-character;
2919    
2920          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2921          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2922    
2923          redo A;          redo A;
2924        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2925          !!!cp (213);          !!!cp (213);
2926          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2927    
2928          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2929          ## reconsume          ## reconsume
2930    
2931          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2932          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2933    
2934          redo A;          redo A;
2935        } else {        } else {
2936          !!!cp (214);          !!!cp (214);
2937          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2938              .= chr $self->{next_char};              .= chr $self->{nc};
2939            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2940                                  length $self->{ct}->{sysid});
2941    
2942          ## Stay in the state          ## Stay in the state
2943          !!!next-input-character;          !!!next-input-character;
2944          redo A;          redo A;
2945        }        }
2946      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2947        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2948          !!!cp (215);          !!!cp (215);
2949          ## Stay in the state          ## Stay in the state
2950          !!!next-input-character;          !!!next-input-character;
2951          redo A;          redo A;
2952        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2953          !!!cp (216);          !!!cp (216);
2954          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2955          !!!next-input-character;          !!!next-input-character;
2956    
2957          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2958    
2959          redo A;          redo A;
2960        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2961          !!!cp (217);          !!!cp (217);
2962            !!!parse-error (type => 'unclosed DOCTYPE');
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;
         if ($self->{next_char} == 0x005D) { # ]  
           !!!next-input-character;  
           if ($self->{next_char} == 0x005D) { # ]  
             !!!next-input-character;  
             MDC: {  
               if ($self->{next_char} == 0x003E) { # >  
                 !!!cp (221.1);  
                 !!!next-input-character;  
                 last CS;  
               } elsif ($self->{next_char} == 0x005D) { # ]  
                 !!!cp (221.2);  
                 $s .= ']';  
                 !!!next-input-character;  
                 redo MDC;  
               } else {  
                 !!!cp (221.3);  
                 $s .= ']]';  
                 #  
               }  
             } # MDC  
           } else {  
             !!!cp (221.4);  
             $s .= ']';  
             #  
           }  
         } else {  
           !!!cp (221.5);  
           #  
         }  
         $s .= chr $self->{next_char};  
3014          !!!next-input-character;          !!!next-input-character;
3015        } # CS          redo A;
3016          } elsif ($self->{nc} == -1) {
3017            $self->{state} = DATA_STATE;
3018            !!!next-input-character;
3019            if (length $self->{ct}->{data}) { # character
3020              !!!cp (221.2);
3021              !!!emit ($self->{ct}); # character
3022            } else {
3023              !!!cp (221.3);
3024              ## No token to emit. $self->{ct} is discarded.
3025            }        
3026            redo A;
3027          } else {
3028            !!!cp (221.4);
3029            $self->{ct}->{data} .= chr $self->{nc};
3030            $self->{read_until}->($self->{ct}->{data},
3031                                  q<]>,
3032                                  length $self->{ct}->{data});
3033    
3034        $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 {
3072            !!!cp (221.11);
3073            $self->{ct}->{data} .= ']]'; # character
3074            $self->{state} = CDATA_SECTION_STATE;
3075            ## Reconsume.
3076            redo A;
3077          }
3078        } elsif ($self->{state} == ENTITY_STATE) {
3079          if ($is_space->{$self->{nc}} or
3080              {
3081                0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3082                $self->{entity_add} => 1,
3083              }->{$self->{nc}}) {
3084            !!!cp (1001);
3085            ## Don't consume
3086            ## No error
3087            ## Return nothing.
3088            #
3089          } elsif ($self->{nc} == 0x0023) { # #
3090            !!!cp (999);
3091            $self->{state} = ENTITY_HASH_STATE;
3092            $self->{s_kwd} = '#';
3093            !!!next-input-character;
3094            redo A;
3095          } elsif ((0x0041 <= $self->{nc} and
3096                    $self->{nc} <= 0x005A) or # A..Z
3097                   (0x0061 <= $self->{nc} and
3098                    $self->{nc} <= 0x007A)) { # a..z
3099            !!!cp (998);
3100            require Whatpm::_NamedEntityList;
3101            $self->{state} = ENTITY_NAME_STATE;
3102            $self->{s_kwd} = chr $self->{nc};
3103            $self->{entity__value} = $self->{s_kwd};
3104            $self->{entity__match} = 0;
3105            !!!next-input-character;
3106            redo A;
3107        } else {        } else {
3108          !!!cp (221.7);          !!!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 => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c);            $self->{nc} <= 0x0039) { # 0..9
           $code = 0xFFFD;  
         } elsif ($code > 0x10FFFF) {  
           !!!cp (1009);  
           !!!parse-error (type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c);  
           $code = 0xFFFD;  
         } elsif ($code == 0x000D) {  
           !!!cp (1010);  
           !!!parse-error (type => 'CR character reference', line => $l, column => $c);  
           $code = 0x000A;  
         } elsif (0x80 <= $code and $code <= 0x9F) {  
           !!!cp (1011);  
           !!!parse-error (type => (sprintf 'C1 character reference:U+%04X', $code), line => $l, column => $c);  
           $code = $c1_entity_char->{$code};  
         }  
   
         return {type => CHARACTER_TOKEN, data => chr $code,  
                 has_reference => 1,  
                 line => $l, column => $c,  
                };  
       } # X  
     } elsif (0x0030 <= $self->{next_char} and  
              $self->{next_char} <= 0x0039) { # 0..9  
       my $code = $self->{next_char} - 0x0030;  
       !!!next-input-character;  
         
       while (0x0030 <= $self->{next_char} and  
                 $self->{next_char} <= 0x0039) { # 0..9  
3181          !!!cp (1012);          !!!cp (1012);
3182          $code *= 10;          $self->{s_kwd} *= 10;
3183          $code += $self->{next_char} - 0x0030;          $self->{s_kwd} += $self->{nc} - 0x0030;
3184                    
3185            ## Stay in the state.
3186          !!!next-input-character;          !!!next-input-character;
3187        }          redo A;
3188          } elsif ($self->{nc} == 0x003B) { # ;
       if ($self->{next_char} == 0x003B) { # ;  
3189          !!!cp (1013);          !!!cp (1013);
3190          !!!next-input-character;          !!!next-input-character;
3191            #
3192        } else {        } else {
3193          !!!cp (1014);          !!!cp (1014);
3194          !!!parse-error (type => 'no refc', line => $l, column => $c);          !!!parse-error (type => 'no refc');
3195            ## Reconsume.
3196            #
3197        }        }
3198    
3199        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3200          my $l = $self->{line_prev};
3201          my $c = $self->{column_prev};
3202          if ($charref_map->{$code}) {
3203          !!!cp (1015);          !!!cp (1015);
3204          !!!parse-error (type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c);          !!!parse-error (type => 'invalid character reference',
3205          $code = 0xFFFD;                          text => (sprintf 'U+%04X', $code),
3206                            line => $l, column => $c);
3207            $code = $charref_map->{$code};
3208        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3209          !!!cp (1016);          !!!cp (1016);
3210          !!!parse-error (type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c);          !!!parse-error (type => 'invalid character reference',
3211                            text => (sprintf 'U-%08X', $code),
3212                            line => $l, column => $c);
3213          $code = 0xFFFD;          $code = 0xFFFD;
       } elsif ($code == 0x000D) {  
         !!!cp (1017);  
         !!!parse-error (type => 'CR character reference', line => $l, column => $c);  
         $code = 0x000A;  
       } elsif (0x80 <= $code and $code <= 0x9F) {  
         !!!cp (1018);  
         !!!parse-error (type => (sprintf 'C1 character reference:U+%04X', $code), line => $l, column => $c);  
         $code = $c1_entity_char->{$code};  
3214        }        }
3215          
3216        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1,        if ($self->{prev_state} == DATA_STATE) {
3217                line => $l, column => $c,          !!!cp (992);
3218               };          $self->{state} = $self->{prev_state};
3219      } else {          ## Reconsume.
3220        !!!cp (1019);          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3221        !!!parse-error (type => 'bare nero', line => $l, column => $c);                    line => $l, column => $c,
3222        !!!back-next-input-character ($self->{next_char});                   });
3223        $self->{next_char} = 0x0023; # #          redo A;
3224        return undef;        } else {
3225      }          !!!cp (991);
3226    } elsif ((0x0041 <= $self->{next_char} and          $self->{ca}->{value} .= chr $code;
3227              $self->{next_char} <= 0x005A) or          $self->{ca}->{has_reference} = 1;
3228             (0x0061 <= $self->{next_char} and          $self->{state} = $self->{prev_state};
3229              $self->{next_char} <= 0x007A)) {          ## Reconsume.
3230      my $entity_name = chr $self->{next_char};          redo A;
3231      !!!next-input-character;        }
3232        } elsif ($self->{state} == HEXREF_X_STATE) {
3233      my $value = $entity_name;        if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3234      my $match = 0;            (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3235      require Whatpm::_NamedEntityList;            (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3236      our $EntityChar;          # 0..9, A..F, a..f
3237            !!!cp (990);
3238      while (length $entity_name < 30 and          $self->{state} = HEXREF_HEX_STATE;
3239             ## NOTE: Some number greater than the maximum length of entity name          $self->{s_kwd} = 0;
3240             ((0x0041 <= $self->{next_char} and # a          ## Reconsume.
3241               $self->{next_char} <= 0x005A) or # x          redo A;
3242              (0x0061 <= $self->{next_char} and # a        } else {
3243               $self->{next_char} <= 0x007A) or # z          !!!parse-error (type => 'bare hcro',
3244              (0x0030 <= $self->{next_char} and # 0                          line => $self->{line_prev},
3245               $self->{next_char} <= 0x0039) or # 9                          column => $self->{column_prev} - 2);
3246              $self->{next_char} == 0x003B)) { # ;  
3247        $entity_name .= chr $self->{next_char};          ## NOTE: According to the spec algorithm, nothing is returned,
3248        if (defined $EntityChar->{$entity_name}) {          ## and then "&#" followed by "X" or "x" is appended to the parent
3249          if ($self->{next_char} == 0x003B) { # ;          ## element or the attribute value in the later processing.
3250            !!!cp (1020);  
3251            $value = $EntityChar->{$entity_name};          if ($self->{prev_state} == DATA_STATE) {
3252            $match = 1;            !!!cp (1005);
3253            !!!next-input-character;            $self->{state} = $self->{prev_state};
3254            last;            ## Reconsume.
3255              !!!emit ({type => CHARACTER_TOKEN,
3256                        data => '&' . $self->{s_kwd},
3257                        line => $self->{line_prev},
3258                        column => $self->{column_prev} - length $self->{s_kwd},
3259                       });
3260              redo A;
3261          } else {          } else {
3262            !!!cp (1021);            !!!cp (989);
3263            $value = $EntityChar->{$entity_name};            $self->{ca}->{value} .= '&' . $self->{s_kwd};
3264            $match = -1;            $self->{state} = $self->{prev_state};
3265              ## Reconsume.
3266              redo A;
3267            }
3268          }
3269        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3270          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3271            # 0..9
3272            !!!cp (1002);
3273            $self->{s_kwd} *= 0x10;
3274            $self->{s_kwd} += $self->{nc} - 0x0030;
3275            ## Stay in the state.
3276            !!!next-input-character;
3277            redo A;
3278          } elsif (0x0061 <= $self->{nc} and
3279                   $self->{nc} <= 0x0066) { # a..f
3280            !!!cp (1003);
3281            $self->{s_kwd} *= 0x10;
3282            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3283            ## Stay in the state.
3284            !!!next-input-character;
3285            redo A;
3286          } elsif (0x0041 <= $self->{nc} and
3287                   $self->{nc} <= 0x0046) { # A..F
3288            !!!cp (1004);
3289            $self->{s_kwd} *= 0x10;
3290            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3291            ## Stay in the state.
3292            !!!next-input-character;
3293            redo A;
3294          } elsif ($self->{nc} == 0x003B) { # ;
3295            !!!cp (1006);
3296            !!!next-input-character;
3297            #
3298          } else {
3299            !!!cp (1007);
3300            !!!parse-error (type => 'no refc',
3301                            line => $self->{line},
3302                            column => $self->{column});
3303            ## Reconsume.
3304            #
3305          }
3306    
3307          my $code = $self->{s_kwd};
3308          my $l = $self->{line_prev};
3309          my $c = $self->{column_prev};
3310          if ($charref_map->{$code}) {
3311            !!!cp (1008);
3312            !!!parse-error (type => 'invalid character reference',
3313                            text => (sprintf 'U+%04X', $code),
3314                            line => $l, column => $c);
3315            $code = $charref_map->{$code};
3316          } elsif ($code > 0x10FFFF) {
3317            !!!cp (1009);
3318            !!!parse-error (type => 'invalid character reference',
3319                            text => (sprintf 'U-%08X', $code),
3320                            line => $l, column => $c);
3321            $code = 0xFFFD;
3322          }
3323    
3324          if ($self->{prev_state} == DATA_STATE) {
3325            !!!cp (988);
3326            $self->{state} = $self->{prev_state};
3327            ## Reconsume.
3328            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3329                      line => $l, column => $c,
3330                     });
3331            redo A;
3332          } else {
3333            !!!cp (987);
3334            $self->{ca}->{value} .= chr $code;
3335            $self->{ca}->{has_reference} = 1;
3336            $self->{state} = $self->{prev_state};
3337            ## Reconsume.
3338            redo A;
3339          }
3340        } elsif ($self->{state} == ENTITY_NAME_STATE) {
3341          if (length $self->{s_kwd} < 30 and
3342              ## NOTE: Some number greater than the maximum length of entity name
3343              ((0x0041 <= $self->{nc} and # a
3344                $self->{nc} <= 0x005A) or # x
3345               (0x0061 <= $self->{nc} and # a
3346                $self->{nc} <= 0x007A) or # z
3347               (0x0030 <= $self->{nc} and # 0
3348                $self->{nc} <= 0x0039) or # 9
3349               $self->{nc} == 0x003B)) { # ;
3350            our $EntityChar;
3351            $self->{s_kwd} .= chr $self->{nc};
3352            if (defined $EntityChar->{$self->{s_kwd}}) {
3353              if ($self->{nc} == 0x003B) { # ;
3354                !!!cp (1020);
3355                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3356                $self->{entity__match} = 1;
3357                !!!next-input-character;
3358                #
3359              } else {
3360                !!!cp (1021);
3361                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3362                $self->{entity__match} = -1;
3363                ## Stay in the state.
3364                !!!next-input-character;
3365                redo A;
3366              }
3367            } else {
3368              !!!cp (1022);
3369              $self->{entity__value} .= chr $self->{nc};
3370              $self->{entity__match} *= 2;
3371              ## Stay in the state.
3372            !!!next-input-character;            !!!next-input-character;
3373              redo A;
3374            }
3375          }
3376    
3377          my $data;
3378          my $has_ref;
3379          if ($self->{entity__match} > 0) {
3380            !!!cp (1023);
3381            $data = $self->{entity__value};
3382            $has_ref = 1;
3383            #
3384          } elsif ($self->{entity__match} < 0) {
3385            !!!parse-error (type => 'no refc');
3386            if ($self->{prev_state} != DATA_STATE and # in attribute
3387                $self->{entity__match} < -1) {
3388              !!!cp (1024);
3389              $data = '&' . $self->{s_kwd};
3390              #
3391            } else {
3392              !!!cp (1025);
3393              $data = $self->{entity__value};
3394              $has_ref = 1;
3395              #
3396          }          }
3397        } else {        } else {
3398          !!!cp (1022);          !!!cp (1026);
3399          $value .= chr $self->{next_char};          !!!parse-error (type => 'bare ero',
3400          $match *= 2;                          line => $self->{line_prev},
3401          !!!next-input-character;                          column => $self->{column_prev} - length $self->{s_kwd});
3402            $data = '&' . $self->{s_kwd};
3403            #
3404        }        }
3405      }    
3406              ## NOTE: In these cases, when a character reference is found,
3407      if ($match > 0) {        ## it is consumed and a character token is returned, or, otherwise,
3408        !!!cp (1023);        ## nothing is consumed and returned, according to the spec algorithm.
3409        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,        ## In this implementation, anything that has been examined by the
3410                line => $l, column => $c,        ## tokenizer is appended to the parent element or the attribute value
3411               };        ## 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 3056  sub _initialize_tree_constructor ($) { Line 3446  sub _initialize_tree_constructor ($) {
3446    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3447    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3448    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3449      $self->{document}->set_user_data (manakai_source_line => 1);
3450      $self->{document}->set_user_data (manakai_source_column => 1);
3451  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3452    
3453  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 3110  sub _tree_construction_initial ($) { Line 3502  sub _tree_construction_initial ($) {
3502        ## language.        ## language.
3503        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
3504        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3505        $doctype_name =~ tr/a-z/A-Z/;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3506        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3507            defined $token->{public_identifier} or            defined $token->{sysid}) {
           defined $token->{system_identifier}) {  
3508          !!!cp ('t1');          !!!cp ('t1');
3509          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
3510        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3511          !!!cp ('t2');          !!!cp ('t2');
         ## ISSUE: ASCII case-insensitive? (in fact it does not matter)  
3512          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
3513          } elsif (defined $token->{pubid}) {
3514            if ($token->{pubid} eq 'XSLT-compat') {
3515              !!!cp ('t1.2');
3516              !!!parse-error (type => 'XSLT-compat', token => $token,
3517                              level => $self->{level}->{should});
3518            } else {
3519              !!!parse-error (type => 'not HTML5', token => $token);
3520            }
3521        } else {        } else {
3522          !!!cp ('t3');          !!!cp ('t3');
3523            #
3524        }        }
3525                
3526        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
3527          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3528        ## NOTE: Default value for both |public_id| and |system_id| attributes        ## NOTE: Default value for both |public_id| and |system_id| attributes
3529        ## 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.
3530        $doctype->public_id ($token->{public_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3531            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};  
3532        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3533        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3534        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
# Line 3139  sub _tree_construction_initial ($) { Line 3536  sub _tree_construction_initial ($) {
3536        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3537          !!!cp ('t4');          !!!cp ('t4');
3538          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3539        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3540          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3541          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3542          if ({          my $prefix = [
3543            "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3544            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3545            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3546            "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 1//",
3547            "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 2//",
3548            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3549            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3550            "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT//",
3551            "-//IETF//DTD HTML 2.0//EN" => 1,            "-//IETF//DTD HTML 2.0//",
3552            "-//IETF//DTD HTML 2.1E//EN" => 1,            "-//IETF//DTD HTML 2.1E//",
3553            "-//IETF//DTD HTML 3.0//EN" => 1,            "-//IETF//DTD HTML 3.0//",
3554            "-//IETF//DTD HTML 3.0//EN//" => 1,            "-//IETF//DTD HTML 3.2 FINAL//",
3555            "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,            "-//IETF//DTD HTML 3.2//",
3556            "-//IETF//DTD HTML 3.2//EN" => 1,            "-//IETF//DTD HTML 3//",
3557            "-//IETF//DTD HTML 3//EN" => 1,            "-//IETF//DTD HTML LEVEL 0//",
3558            "-//IETF//DTD HTML LEVEL 0//EN" => 1,            "-//IETF//DTD HTML LEVEL 1//",
3559            "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,            "-//IETF//DTD HTML LEVEL 2//",
3560            "-//IETF//DTD HTML LEVEL 1//EN" => 1,            "-//IETF//DTD HTML LEVEL 3//",
3561            "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 0//",
3562            "-//IETF//DTD HTML LEVEL 2//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 1//",
3563            "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 2//",
3564            "-//IETF//DTD HTML LEVEL 3//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 3//",
3565            "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,            "-//IETF//DTD HTML STRICT//",
3566            "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,            "-//IETF//DTD HTML//",
3567            "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,            "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3568            "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3569            "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3570            "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3571            "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3572            "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3573            "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3574            "-//IETF//DTD HTML STRICT//EN" => 1,            "-//NETSCAPE COMM. CORP.//DTD HTML//",
3575            "-//IETF//DTD HTML STRICT//EN//2.0" => 1,            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3576            "-//IETF//DTD HTML STRICT//EN//3.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3577            "-//IETF//DTD HTML//EN" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3578            "-//IETF//DTD HTML//EN//2.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3579            "-//IETF//DTD HTML//EN//3.0" => 1,            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3580            "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3581            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3582            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3583            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3584            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3585            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,            "-//W3C//DTD HTML 3 1995-03-24//",
3586            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,            "-//W3C//DTD HTML 3.2 DRAFT//",
3587            "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,            "-//W3C//DTD HTML 3.2 FINAL//",
3588            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,            "-//W3C//DTD HTML 3.2//",
3589            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,            "-//W3C//DTD HTML 3.2S DRAFT//",
3590            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 FRAMESET//",
3591            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3592            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3593            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3594            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,            "-//W3C//DTD W3 HTML//",
3595            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,            "-//W3O//DTD W3 HTML 3.0//",
3596            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3597            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML//",
3598            "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,          ]; # $prefix
3599            "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,          my $match;
3600            "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,          for (@$prefix) {
3601            "-//W3C//DTD HTML 3.2//EN" => 1,            if (substr ($prefix, 0, length $_) eq $_) {
3602            "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,              $match = 1;
3603            "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,              last;
3604            "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,            }
3605            "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,          }
3606            "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,          if ($match or
3607            "-//W3C//DTD W3 HTML//EN" => 1,              $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3608            "-//W3O//DTD W3 HTML 3.0//EN" => 1,              $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3609            "-//W3O//DTD W3 HTML 3.0//EN//" => 1,              $pubid eq "HTML") {
           "-//W3O//DTD W3 HTML STRICT 3.0//EN//" => 1,  
           "-//WEBTECHS//DTD MOZILLA HTML 2.0//EN" => 1,  
           "-//WEBTECHS//DTD MOZILLA HTML//EN" => 1,  
           "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" => 1,  
           "HTML" => 1,  
         }->{$pubid}) {  
3610            !!!cp ('t5');            !!!cp ('t5');
3611            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3612          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3613                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3614            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3615              !!!cp ('t6');              !!!cp ('t6');
3616              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3617            } else {            } else {
3618              !!!cp ('t7');              !!!cp ('t7');
3619              $self->{document}->manakai_compat_mode ('limited quirks');              $self->{document}->manakai_compat_mode ('limited quirks');
3620            }            }
3621          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3622                   $pubid eq "-//W3C//DTD XHTML 1.0 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3623            !!!cp ('t8');            !!!cp ('t8');
3624            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3625          } else {          } else {
# Line 3237  sub _tree_construction_initial ($) { Line 3628  sub _tree_construction_initial ($) {
3628        } else {        } else {
3629          !!!cp ('t10');          !!!cp ('t10');
3630        }        }
3631        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3632          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3633          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3634          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
3635            ## TODO: Check the spec: PUBLIC "(limited quirks)" "(quirks)"            ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3636              ## marked as quirks.
3637            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3638            !!!cp ('t11');            !!!cp ('t11');
3639          } else {          } else {
# Line 3267  sub _tree_construction_initial ($) { Line 3659  sub _tree_construction_initial ($) {
3659        !!!ack-later;        !!!ack-later;
3660        return;        return;
3661      } elsif ($token->{type} == CHARACTER_TOKEN) {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3662        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3663          ## Ignore the token          ## Ignore the token
3664    
3665          unless (length $token->{data}) {          unless (length $token->{data}) {
# Line 3324  sub _tree_construction_root_element ($) Line 3716  sub _tree_construction_root_element ($)
3716          !!!next-token;          !!!next-token;
3717          redo B;          redo B;
3718        } elsif ($token->{type} == CHARACTER_TOKEN) {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3719          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3720            ## Ignore the token.            ## Ignore the token.
3721    
3722            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 3391  sub _tree_construction_root_element ($) Line 3783  sub _tree_construction_root_element ($)
3783      ## NOTE: Reprocess the token.      ## NOTE: Reprocess the token.
3784      !!!ack-later;      !!!ack-later;
3785      return; ## Go to the "before head" insertion mode.      return; ## Go to the "before head" insertion mode.
   
     ## ISSUE: There is an issue in the spec  
3786    } # B    } # B
3787    
3788    die "$0: _tree_construction_root_element: This should never be reached";    die "$0: _tree_construction_root_element: This should never be reached";
# Line 3427  sub _reset_insertion_mode ($) { Line 3817  sub _reset_insertion_mode ($) {
3817          ## NOTE: Strictly spaking, the line below only applies to MathML and          ## NOTE: Strictly spaking, the line below only applies to MathML and
3818          ## SVG elements.  Currently the HTML syntax supports only MathML and          ## SVG elements.  Currently the HTML syntax supports only MathML and
3819          ## SVG elements as foreigners.          ## SVG elements as foreigners.
3820          $new_mode = $self->{insertion_mode} | IN_FOREIGN_CONTENT_IM;          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
         ## ISSUE: What is set as the secondary insertion mode?  
3821        } elsif ($node->[1] & TABLE_CELL_EL) {        } elsif ($node->[1] & TABLE_CELL_EL) {
3822          if ($last) {          if ($last) {
3823            !!!cp ('t28.2');            !!!cp ('t28.2');
# Line 3629  sub _tree_construction_main ($) { Line 4018  sub _tree_construction_main ($) {
4018        ## NOTE: An end-of-file token.        ## NOTE: An end-of-file token.
4019        if ($content_model_flag == CDATA_CONTENT_MODEL) {        if ($content_model_flag == CDATA_CONTENT_MODEL) {
4020          !!!cp ('t43');          !!!cp ('t43');
4021          !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token);          !!!parse-error (type => 'in CDATA:#eof', token => $token);
4022        } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {        } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
4023          !!!cp ('t44');          !!!cp ('t44');
4024          !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token);          !!!parse-error (type => 'in RCDATA:#eof', token => $token);
4025        } else {        } else {
4026          die "$0: $content_model_flag in parse_rcdata";          die "$0: $content_model_flag in parse_rcdata";
4027        }        }
# Line 3669  sub _tree_construction_main ($) { Line 4058  sub _tree_construction_main ($) {
4058        ## Ignore the token        ## Ignore the token
4059      } else {      } else {
4060        !!!cp ('t48');        !!!cp ('t48');
4061        !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token);        !!!parse-error (type => 'in CDATA:#eof', token => $token);
4062        ## ISSUE: And ignore?        ## ISSUE: And ignore?
4063        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4064      }      }
# Line 3720  sub _tree_construction_main ($) { Line 4109  sub _tree_construction_main ($) {
4109        } # AFE        } # AFE
4110        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4111          !!!cp ('t53');          !!!cp ('t53');
4112          !!!parse-error (type => 'unmatched end tag:'.$tag_name, token => $end_tag_token);          !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4113          ## Ignore the token          ## Ignore the token
4114          !!!next-token;          !!!next-token;
4115          return;          return;
# Line 3737  sub _tree_construction_main ($) { Line 4126  sub _tree_construction_main ($) {
4126              last INSCOPE;              last INSCOPE;
4127            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4128              !!!cp ('t55');              !!!cp ('t55');
4129              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},              !!!parse-error (type => 'unmatched end tag',
4130                                text => $token->{tag_name},
4131                              token => $end_tag_token);                              token => $end_tag_token);
4132              ## Ignore the token              ## Ignore the token
4133              !!!next-token;              !!!next-token;
# Line 3750  sub _tree_construction_main ($) { Line 4140  sub _tree_construction_main ($) {
4140        } # INSCOPE        } # INSCOPE
4141        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4142          !!!cp ('t57');          !!!cp ('t57');
4143          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},          !!!parse-error (type => 'unmatched end tag',
4144                            text => $token->{tag_name},
4145                          token => $end_tag_token);                          token => $end_tag_token);
4146          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4147          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
# Line 3759  sub _tree_construction_main ($) { Line 4150  sub _tree_construction_main ($) {
4150        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4151          !!!cp ('t58');          !!!cp ('t58');
4152          !!!parse-error (type => 'not closed',          !!!parse-error (type => 'not closed',
4153                          value => $self->{open_elements}->[-1]->[0]                          text => $self->{open_elements}->[-1]->[0]
4154                              ->manakai_local_name,                              ->manakai_local_name,
4155                          token => $end_tag_token);                          token => $end_tag_token);
4156        }        }
# Line 3968  sub _tree_construction_main ($) { Line 4359  sub _tree_construction_main ($) {
4359    B: while (1) {    B: while (1) {
4360      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
4361        !!!cp ('t73');        !!!cp ('t73');
4362        !!!parse-error (type => 'DOCTYPE in the middle', token => $token);        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4363        ## Ignore the token        ## Ignore the token
4364        ## Stay in the phase        ## Stay in the phase
4365        !!!next-token;        !!!next-token;
# Line 3977  sub _tree_construction_main ($) { Line 4368  sub _tree_construction_main ($) {
4368               $token->{tag_name} eq 'html') {               $token->{tag_name} eq 'html') {
4369        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4370          !!!cp ('t79');          !!!cp ('t79');
4371          !!!parse-error (type => 'after html:html', token => $token);          !!!parse-error (type => 'after html', text => 'html', token => $token);
4372          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
4373        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4374          !!!cp ('t80');          !!!cp ('t80');
4375          !!!parse-error (type => 'after html:html', token => $token);          !!!parse-error (type => 'after html', text => 'html', token => $token);
4376          $self->{insertion_mode} = AFTER_FRAMESET_IM;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4377        } else {        } else {
4378          !!!cp ('t81');          !!!cp ('t81');
# Line 4032  sub _tree_construction_main ($) { Line 4423  sub _tree_construction_main ($) {
4423            #            #
4424          } elsif ({          } elsif ({
4425                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4426                    center => 1, code => 1, dd => 1, div => 1, dl => 1, em => 1,                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4427                    embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1, ## No h4!                    em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4428                    h5 => 1, h6 => 1, head => 1, hr => 1, i => 1, img => 1,                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4429                    li => 1, menu => 1, meta => 1, nobr => 1, p => 1, pre => 1,                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4430                    ruby => 1, s => 1, small => 1, span => 1, strong => 1,                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4431                    sub => 1, sup => 1, table => 1, tt => 1, u => 1, ul => 1,                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4432                    var => 1,                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4433                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
4434            !!!cp ('t87.2');            !!!cp ('t87.2');
4435            !!!parse-error (type => 'not closed',            !!!parse-error (type => 'not closed',
4436                            value => $self->{open_elements}->[-1]->[0]                            text => $self->{open_elements}->[-1]->[0]
4437                                ->manakai_local_name,                                ->manakai_local_name,
4438                            token => $token);                            token => $token);
4439    
# Line 4118  sub _tree_construction_main ($) { Line 4509  sub _tree_construction_main ($) {
4509          !!!cp ('t87.5');          !!!cp ('t87.5');
4510          #          #
4511        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
         ## NOTE: "using the rules for secondary insertion mode" then "continue"  
4512          !!!cp ('t87.6');          !!!cp ('t87.6');
4513          #          !!!parse-error (type => 'not closed',
4514          ## TODO: ...                          text => $self->{open_elements}->[-1]->[0]
4515                                ->manakai_local_name,
4516                            token => $token);
4517    
4518            pop @{$self->{open_elements}}
4519                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4520    
4521            ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
4522    
4523            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4524            ## Reprocess.
4525            next B;
4526        } else {        } else {
4527          die "$0: $token->{type}: Unknown token type";                  die "$0: $token->{type}: Unknown token type";        
4528        }        }
# Line 4129  sub _tree_construction_main ($) { Line 4530  sub _tree_construction_main ($) {
4530    
4531      if ($self->{insertion_mode} & HEAD_IMS) {      if ($self->{insertion_mode} & HEAD_IMS) {
4532        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4533          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4534            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4535              !!!cp ('t88.2');              !!!cp ('t88.2');
4536              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4537                #
4538            } else {            } else {
4539              !!!cp ('t88.1');              !!!cp ('t88.1');
4540              ## Ignore the token.              ## Ignore the token.
4541              !!!next-token;              #
             next B;  
4542            }            }
4543            unless (length $token->{data}) {            unless (length $token->{data}) {
4544              !!!cp ('t88');              !!!cp ('t88');
4545              !!!next-token;              !!!next-token;
4546              next B;              next B;
4547            }            }
4548    ## TODO: set $token->{column} appropriately
4549          }          }
4550    
4551          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
# Line 4162  sub _tree_construction_main ($) { Line 4564  sub _tree_construction_main ($) {
4564            !!!cp ('t90');            !!!cp ('t90');
4565            ## As if </noscript>            ## As if </noscript>
4566            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4567            !!!parse-error (type => 'in noscript:#character', token => $token);            !!!parse-error (type => 'in noscript:#text', token => $token);
4568                        
4569            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4570            ## As if </head>            ## As if </head>
# Line 4199  sub _tree_construction_main ($) { Line 4601  sub _tree_construction_main ($) {
4601              next B;              next B;
4602            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4603              !!!cp ('t93.2');              !!!cp ('t93.2');
4604              !!!parse-error (type => 'after head:head', token => $token); ## TODO: error type              !!!parse-error (type => 'after head', text => 'head',
4605                                token => $token);
4606              ## Ignore the token              ## Ignore the token
4607              !!!nack ('t93.3');              !!!nack ('t93.3');
4608              !!!next-token;              !!!next-token;
4609              next B;              next B;
4610            } else {            } else {
4611              !!!cp ('t95');              !!!cp ('t95');
4612              !!!parse-error (type => 'in head:head', token => $token); # or in head noscript              !!!parse-error (type => 'in head:head',
4613                                token => $token); # or in head noscript
4614              ## Ignore the token              ## Ignore the token
4615              !!!nack ('t95.1');              !!!nack ('t95.1');
4616              !!!next-token;              !!!next-token;
# Line 4231  sub _tree_construction_main ($) { Line 4635  sub _tree_construction_main ($) {
4635                  !!!cp ('t98');                  !!!cp ('t98');
4636                  ## As if </noscript>                  ## As if </noscript>
4637                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4638                  !!!parse-error (type => 'in noscript:base', token => $token);                  !!!parse-error (type => 'in noscript', text => 'base',
4639                                    token => $token);
4640                                
4641                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4642                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
# Line 4242  sub _tree_construction_main ($) { Line 4647  sub _tree_construction_main ($) {
4647                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4648                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4649                  !!!cp ('t100');                  !!!cp ('t100');
4650                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4651                                    text => $token->{tag_name}, token => $token);
4652                  push @{$self->{open_elements}},                  push @{$self->{open_elements}},
4653                      [$self->{head_element}, $el_category->{head}];                      [$self->{head_element}, $el_category->{head}];
4654                } else {                } else {
4655                  !!!cp ('t101');                  !!!cp ('t101');
4656                }                }
4657                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4658                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                pop @{$self->{open_elements}};
4659                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4660                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4661                !!!nack ('t101.1');                !!!nack ('t101.1');
4662                !!!next-token;                !!!next-token;
4663                next B;                next B;
4664              } elsif ($token->{tag_name} eq 'link') {          } elsif ($token->{tag_name} eq 'link') {
4665                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4666                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4667                  !!!cp ('t102');              !!!cp ('t102');
4668                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'after head',
4669                  push @{$self->{open_elements}},                              text => $token->{tag_name}, token => $token);
4670                      [$self->{head_element}, $el_category->{head}];              push @{$self->{open_elements}},
4671                } else {                  [$self->{head_element}, $el_category->{head}];
4672                  !!!cp ('t103');            } else {
4673                }              !!!cp ('t103');
4674                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            }
4675                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4676                pop @{$self->{open_elements}} # <head>            pop @{$self->{open_elements}};
4677                    if $self->{insertion_mode} == AFTER_HEAD_IM;            pop @{$self->{open_elements}} # <head>
4678                !!!ack ('t103.1');                if $self->{insertion_mode} == AFTER_HEAD_IM;
4679                !!!next-token;            !!!ack ('t103.1');
4680                next B;            !!!next-token;
4681              next B;
4682            } elsif ($token->{tag_name} eq 'command' or
4683                     $token->{tag_name} eq 'eventsource') {
4684              if ($self->{insertion_mode} == IN_HEAD_IM) {
4685                ## NOTE: If the insertion mode at the time of the emission
4686                ## of the token was "before head", $self->{insertion_mode}
4687                ## is already changed to |IN_HEAD_IM|.
4688    
4689                ## NOTE: There is a "as if in head" code clone.
4690                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4691                pop @{$self->{open_elements}};
4692                pop @{$self->{open_elements}} # <head>
4693                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4694                !!!ack ('t103.2');
4695                !!!next-token;
4696                next B;
4697              } else {
4698                ## NOTE: "in head noscript" or "after head" insertion mode
4699                ## - in these cases, these tags are treated as same as
4700                ## normal in-body tags.
4701                !!!cp ('t103.3');
4702                #
4703              }
4704              } elsif ($token->{tag_name} eq 'meta') {              } elsif ($token->{tag_name} eq 'meta') {
4705                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4706                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4707                  !!!cp ('t104');                  !!!cp ('t104');
4708                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4709                                    text => $token->{tag_name}, token => $token);
4710                  push @{$self->{open_elements}},                  push @{$self->{open_elements}},
4711                      [$self->{head_element}, $el_category->{head}];                      [$self->{head_element}, $el_category->{head}];
4712                } else {                } else {
4713                  !!!cp ('t105');                  !!!cp ('t105');
4714                }                }
4715                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4716                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                my $meta_el = pop @{$self->{open_elements}};
4717    
4718                unless ($self->{confident}) {                unless ($self->{confident}) {
4719                  if ($token->{attributes}->{charset}) {                  if ($token->{attributes}->{charset}) {
# Line 4300  sub _tree_construction_main ($) { Line 4730  sub _tree_construction_main ($) {
4730                                                 ->{has_reference});                                                 ->{has_reference});
4731                  } elsif ($token->{attributes}->{content}) {                  } elsif ($token->{attributes}->{content}) {
4732                    if ($token->{attributes}->{content}->{value}                    if ($token->{attributes}->{content}->{value}
4733                        =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4734                            [\x09-\x0D\x20]*=                            [\x09\x0A\x0C\x0D\x20]*=
4735                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4736                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                            ([^"'\x09\x0A\x0C\x0D\x20]
4737                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4738                      !!!cp ('t107');                      !!!cp ('t107');
4739                      ## NOTE: Whether the encoding is supported or not is handled                      ## NOTE: Whether the encoding is supported or not is handled
4740                      ## in the {change_encoding} callback.                      ## in the {change_encoding} callback.
# Line 4345  sub _tree_construction_main ($) { Line 4776  sub _tree_construction_main ($) {
4776                  !!!cp ('t111');                  !!!cp ('t111');
4777                  ## As if </noscript>                  ## As if </noscript>
4778                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4779                  !!!parse-error (type => 'in noscript:title', token => $token);                  !!!parse-error (type => 'in noscript', text => 'title',
4780                                    token => $token);
4781                                
4782                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4783                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4784                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4785                  !!!cp ('t112');                  !!!cp ('t112');
4786                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4787                                    text => $token->{tag_name}, token => $token);
4788                  push @{$self->{open_elements}},                  push @{$self->{open_elements}},
4789                      [$self->{head_element}, $el_category->{head}];                      [$self->{head_element}, $el_category->{head}];
4790                } else {                } else {
# Line 4365  sub _tree_construction_main ($) { Line 4798  sub _tree_construction_main ($) {
4798                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4799                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4800                next B;                next B;
4801              } elsif ($token->{tag_name} eq 'style') {              } elsif ($token->{tag_name} eq 'style' or
4802                         $token->{tag_name} eq 'noframes') {
4803                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4804                ## insertion mode IN_HEAD_IM)                ## insertion mode IN_HEAD_IM)
4805                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4806                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4807                  !!!cp ('t114');                  !!!cp ('t114');
4808                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4809                                    text => $token->{tag_name}, token => $token);
4810                  push @{$self->{open_elements}},                  push @{$self->{open_elements}},
4811                      [$self->{head_element}, $el_category->{head}];                      [$self->{head_element}, $el_category->{head}];
4812                } else {                } else {
# Line 4392  sub _tree_construction_main ($) { Line 4827  sub _tree_construction_main ($) {
4827                  next B;                  next B;
4828                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4829                  !!!cp ('t117');                  !!!cp ('t117');
4830                  !!!parse-error (type => 'in noscript:noscript', token => $token);                  !!!parse-error (type => 'in noscript', text => 'noscript',
4831                                    token => $token);
4832                  ## Ignore the token                  ## Ignore the token
4833                  !!!nack ('t117.1');                  !!!nack ('t117.1');
4834                  !!!next-token;                  !!!next-token;
# Line 4406  sub _tree_construction_main ($) { Line 4842  sub _tree_construction_main ($) {
4842                  !!!cp ('t119');                  !!!cp ('t119');
4843                  ## As if </noscript>                  ## As if </noscript>
4844                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4845                  !!!parse-error (type => 'in noscript:script', token => $token);                  !!!parse-error (type => 'in noscript', text => 'script',
4846                                    token => $token);
4847                                
4848                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4849                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4850                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4851                  !!!cp ('t120');                  !!!cp ('t120');
4852                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4853                                    text => $token->{tag_name}, token => $token);
4854                  push @{$self->{open_elements}},                  push @{$self->{open_elements}},
4855                      [$self->{head_element}, $el_category->{head}];                      [$self->{head_element}, $el_category->{head}];
4856                } else {                } else {
# Line 4430  sub _tree_construction_main ($) { Line 4868  sub _tree_construction_main ($) {
4868                  !!!cp ('t122');                  !!!cp ('t122');
4869                  ## As if </noscript>                  ## As if </noscript>
4870                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4871                  !!!parse-error (type => 'in noscript:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'in noscript',
4872                                    text => $token->{tag_name}, token => $token);
4873                                    
4874                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4875                  ## As if </head>                  ## As if </head>
# Line 4469  sub _tree_construction_main ($) { Line 4908  sub _tree_construction_main ($) {
4908                !!!cp ('t129');                !!!cp ('t129');
4909                ## As if </noscript>                ## As if </noscript>
4910                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4911                !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'in noscript:/',
4912                                  text => $token->{tag_name}, token => $token);
4913                                
4914                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
4915                ## As if </head>                ## As if </head>
# Line 4512  sub _tree_construction_main ($) { Line 4952  sub _tree_construction_main ($) {
4952                  !!!cp ('t133');                  !!!cp ('t133');
4953                  ## As if </noscript>                  ## As if </noscript>
4954                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4955                  !!!parse-error (type => 'in noscript:/head', token => $token);                  !!!parse-error (type => 'in noscript:/',
4956                                    text => 'head', token => $token);
4957                                    
4958                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4959                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
# Line 4527  sub _tree_construction_main ($) { Line 4968  sub _tree_construction_main ($) {
4968                  next B;                  next B;
4969                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4970                  !!!cp ('t134.1');                  !!!cp ('t134.1');
4971                  !!!parse-error (type => 'unmatched end tag:head', token => $token);                  !!!parse-error (type => 'unmatched end tag', text => 'head',
4972                                    token => $token);
4973                  ## Ignore the token                  ## Ignore the token
4974                  !!!next-token;                  !!!next-token;
4975                  next B;                  next B;
# Line 4544  sub _tree_construction_main ($) { Line 4986  sub _tree_construction_main ($) {
4986                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
4987                         $self->{insertion_mode} == AFTER_HEAD_IM) {                         $self->{insertion_mode} == AFTER_HEAD_IM) {
4988                  !!!cp ('t137');                  !!!cp ('t137');
4989                  !!!parse-error (type => 'unmatched end tag:noscript', token => $token);                  !!!parse-error (type => 'unmatched end tag',
4990                                    text => 'noscript', token => $token);
4991                  ## Ignore the token ## ISSUE: An issue in the spec.                  ## Ignore the token ## ISSUE: An issue in the spec.
4992                  !!!next-token;                  !!!next-token;
4993                  next B;                  next B;
# Line 4559  sub _tree_construction_main ($) { Line 5002  sub _tree_construction_main ($) {
5002                    $self->{insertion_mode} == IN_HEAD_IM or                    $self->{insertion_mode} == IN_HEAD_IM or
5003                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5004                  !!!cp ('t140');                  !!!cp ('t140');
5005                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5006                                    text => $token->{tag_name}, token => $token);
5007                  ## Ignore the token                  ## Ignore the token
5008                  !!!next-token;                  !!!next-token;
5009                  next B;                  next B;
5010                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5011                  !!!cp ('t140.1');                  !!!cp ('t140.1');
5012                  !!!parse-error (type => 'unmatched end tag:' . $token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5013                                    text => $token->{tag_name}, token => $token);
5014                  ## Ignore the token                  ## Ignore the token
5015                  !!!next-token;                  !!!next-token;
5016                  next B;                  next B;
# Line 4574  sub _tree_construction_main ($) { Line 5019  sub _tree_construction_main ($) {
5019                }                }
5020              } elsif ($token->{tag_name} eq 'p') {              } elsif ($token->{tag_name} eq 'p') {
5021                !!!cp ('t142');                !!!cp ('t142');
5022                !!!parse-error (type => 'unmatched end tag:p', token => $token);                !!!parse-error (type => 'unmatched end tag',
5023                                  text => $token->{tag_name}, token => $token);
5024                ## Ignore the token                ## Ignore the token
5025                !!!next-token;                !!!next-token;
5026                next B;                next B;
# Line 4597  sub _tree_construction_main ($) { Line 5043  sub _tree_construction_main ($) {
5043                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5044                  !!!cp ('t143.3');                  !!!cp ('t143.3');
5045                  ## ISSUE: Two parse errors for <head><noscript></br>                  ## ISSUE: Two parse errors for <head><noscript></br>
5046                  !!!parse-error (type => 'unmatched end tag:br', token => $token);                  !!!parse-error (type => 'unmatched end tag',
5047                                    text => 'br', token => $token);
5048                  ## As if </noscript>                  ## As if </noscript>
5049                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5050                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
# Line 4616  sub _tree_construction_main ($) { Line 5063  sub _tree_construction_main ($) {
5063                }                }
5064    
5065                ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.                ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5066                !!!parse-error (type => 'unmatched end tag:br', token => $token);                !!!parse-error (type => 'unmatched end tag',
5067                                  text => 'br', token => $token);
5068                ## Ignore the token                ## Ignore the token
5069                !!!next-token;                !!!next-token;
5070                next B;                next B;
5071              } else {              } else {
5072                !!!cp ('t145');                !!!cp ('t145');
5073                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
5074                                  text => $token->{tag_name}, token => $token);
5075                ## Ignore the token                ## Ignore the token
5076                !!!next-token;                !!!next-token;
5077                next B;                next B;
# Line 4632  sub _tree_construction_main ($) { Line 5081  sub _tree_construction_main ($) {
5081                !!!cp ('t146');                !!!cp ('t146');
5082                ## As if </noscript>                ## As if </noscript>
5083                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5084                !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'in noscript:/',
5085                                  text => $token->{tag_name}, token => $token);
5086                                
5087                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
5088                ## As if </head>                ## As if </head>
# Line 4648  sub _tree_construction_main ($) { Line 5098  sub _tree_construction_main ($) {
5098              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5099  ## ISSUE: This case cannot be reached?  ## ISSUE: This case cannot be reached?
5100                !!!cp ('t148');                !!!cp ('t148');
5101                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
5102                                  text => $token->{tag_name}, token => $token);
5103                ## Ignore the token ## ISSUE: An issue in the spec.                ## Ignore the token ## ISSUE: An issue in the spec.
5104                !!!next-token;                !!!next-token;
5105                next B;                next B;
# Line 4719  sub _tree_construction_main ($) { Line 5170  sub _tree_construction_main ($) {
5170        } else {        } else {
5171          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5172        }        }
   
           ## ISSUE: An issue in the spec.  
5173      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
5174            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
5175              !!!cp ('t150');              !!!cp ('t150');
# Line 4759  sub _tree_construction_main ($) { Line 5208  sub _tree_construction_main ($) {
5208    
5209                  !!!cp ('t153');                  !!!cp ('t153');
5210                  !!!parse-error (type => 'start tag not allowed',                  !!!parse-error (type => 'start tag not allowed',
5211                      value => $token->{tag_name}, token => $token);                      text => $token->{tag_name}, token => $token);
5212                  ## Ignore the token                  ## Ignore the token
5213                  !!!nack ('t153.1');                  !!!nack ('t153.1');
5214                  !!!next-token;                  !!!next-token;
5215                  next B;                  next B;
5216                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5217                  !!!parse-error (type => 'not closed:caption', token => $token);                  !!!parse-error (type => 'not closed', text => 'caption',
5218                                    token => $token);
5219                                    
5220                  ## NOTE: As if </caption>.                  ## NOTE: As if </caption>.
5221                  ## have a table element in table scope                  ## have a table element in table scope
# Line 4785  sub _tree_construction_main ($) { Line 5235  sub _tree_construction_main ($) {
5235    
5236                    !!!cp ('t157');                    !!!cp ('t157');
5237                    !!!parse-error (type => 'start tag not allowed',                    !!!parse-error (type => 'start tag not allowed',
5238                                    value => $token->{tag_name}, token => $token);                                    text => $token->{tag_name}, token => $token);
5239                    ## Ignore the token                    ## Ignore the token
5240                    !!!nack ('t157.1');                    !!!nack ('t157.1');
5241                    !!!next-token;                    !!!next-token;
# Line 4802  sub _tree_construction_main ($) { Line 5252  sub _tree_construction_main ($) {
5252                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5253                    !!!cp ('t159');                    !!!cp ('t159');
5254                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
5255                                    value => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
5256                                        ->manakai_local_name,                                        ->manakai_local_name,
5257                                    token => $token);                                    token => $token);
5258                  } else {                  } else {
# Line 4844  sub _tree_construction_main ($) { Line 5294  sub _tree_construction_main ($) {
5294                  } # INSCOPE                  } # INSCOPE
5295                    unless (defined $i) {                    unless (defined $i) {
5296                      !!!cp ('t165');                      !!!cp ('t165');
5297                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                      !!!parse-error (type => 'unmatched end tag',
5298                                        text => $token->{tag_name},
5299                                        token => $token);
5300                      ## Ignore the token                      ## Ignore the token
5301                      !!!next-token;                      !!!next-token;
5302                      next B;                      next B;
# Line 4861  sub _tree_construction_main ($) { Line 5313  sub _tree_construction_main ($) {
5313                          ne $token->{tag_name}) {                          ne $token->{tag_name}) {
5314                    !!!cp ('t167');                    !!!cp ('t167');
5315                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
5316                                    value => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
5317                                        ->manakai_local_name,                                        ->manakai_local_name,
5318                                    token => $token);                                    token => $token);
5319                  } else {                  } else {
# Line 4878  sub _tree_construction_main ($) { Line 5330  sub _tree_construction_main ($) {
5330                  next B;                  next B;
5331                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5332                  !!!cp ('t169');                  !!!cp ('t169');
5333                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5334                                    text => $token->{tag_name}, token => $token);
5335                  ## Ignore the token                  ## Ignore the token
5336                  !!!next-token;                  !!!next-token;
5337                  next B;                  next B;
# Line 4905  sub _tree_construction_main ($) { Line 5358  sub _tree_construction_main ($) {
5358    
5359                    !!!cp ('t173');                    !!!cp ('t173');
5360                    !!!parse-error (type => 'unmatched end tag',                    !!!parse-error (type => 'unmatched end tag',
5361                                    value => $token->{tag_name}, token => $token);                                    text => $token->{tag_name}, token => $token);
5362                    ## Ignore the token                    ## Ignore the token
5363                    !!!next-token;                    !!!next-token;
5364                    next B;                    next B;
# Line 4921  sub _tree_construction_main ($) { Line 5374  sub _tree_construction_main ($) {
5374                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5375                    !!!cp ('t175');                    !!!cp ('t175');
5376                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
5377                                    value => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
5378                                        ->manakai_local_name,                                        ->manakai_local_name,
5379                                    token => $token);                                    token => $token);
5380                  } else {                  } else {
# Line 4938  sub _tree_construction_main ($) { Line 5391  sub _tree_construction_main ($) {
5391                  next B;                  next B;
5392                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5393                  !!!cp ('t177');                  !!!cp ('t177');
5394                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5395                                    text => $token->{tag_name}, token => $token);
5396                  ## Ignore the token                  ## Ignore the token
5397                  !!!next-token;                  !!!next-token;
5398                  next B;                  next B;
# Line 4981  sub _tree_construction_main ($) { Line 5435  sub _tree_construction_main ($) {
5435    
5436                  !!!cp ('t182');                  !!!cp ('t182');
5437                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
5438                      value => $token->{tag_name}, token => $token);                      text => $token->{tag_name}, token => $token);
5439                  ## Ignore the token                  ## Ignore the token
5440                  !!!next-token;                  !!!next-token;
5441                  next B;                  next B;
5442                } # INSCOPE                } # INSCOPE
5443              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
5444                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5445                !!!parse-error (type => 'not closed:caption', token => $token);                !!!parse-error (type => 'not closed', text => 'caption',
5446                                  token => $token);
5447    
5448                ## As if </caption>                ## As if </caption>
5449                ## have a table element in table scope                ## have a table element in table scope
# Line 5006  sub _tree_construction_main ($) { Line 5461  sub _tree_construction_main ($) {
5461                } # INSCOPE                } # INSCOPE
5462                unless (defined $i) {                unless (defined $i) {
5463                  !!!cp ('t186');                  !!!cp ('t186');
5464                  !!!parse-error (type => 'unmatched end tag:caption', token => $token);                  !!!parse-error (type => 'unmatched end tag',
5465                                    text => 'caption', token => $token);
5466                  ## Ignore the token                  ## Ignore the token
5467                  !!!next-token;                  !!!next-token;
5468                  next B;                  next B;
# Line 5021  sub _tree_construction_main ($) { Line 5477  sub _tree_construction_main ($) {
5477                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5478                  !!!cp ('t188');                  !!!cp ('t188');
5479                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
5480                                  value => $self->{open_elements}->[-1]->[0]                                  text => $self->{open_elements}->[-1]->[0]
5481                                      ->manakai_local_name,                                      ->manakai_local_name,
5482                                  token => $token);                                  token => $token);
5483                } else {                } else {
# Line 5041  sub _tree_construction_main ($) { Line 5497  sub _tree_construction_main ($) {
5497                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5498                if ($self->{insertion_mode} & BODY_TABLE_IMS) {                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5499                  !!!cp ('t190');                  !!!cp ('t190');
5500                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5501                                    text => $token->{tag_name}, token => $token);
5502                  ## Ignore the token                  ## Ignore the token
5503                  !!!next-token;                  !!!next-token;
5504                  next B;                  next B;
# Line 5055  sub _tree_construction_main ($) { Line 5512  sub _tree_construction_main ($) {
5512                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5513                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5514                !!!cp ('t192');                !!!cp ('t192');
5515                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
5516                                  text => $token->{tag_name}, token => $token);
5517                ## Ignore the token                ## Ignore the token
5518                !!!next-token;                !!!next-token;
5519                next B;                next B;
# Line 5083  sub _tree_construction_main ($) { Line 5541  sub _tree_construction_main ($) {
5541      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
5542        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
5543          if (not $open_tables->[-1]->[1] and # tainted          if (not $open_tables->[-1]->[1] and # tainted
5544              $token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5545            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5546                                
5547            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 5095  sub _tree_construction_main ($) { Line 5553  sub _tree_construction_main ($) {
5553            }            }
5554          }          }
5555    
5556              !!!parse-error (type => 'in table:#character', token => $token);          !!!parse-error (type => 'in table:#text', token => $token);
5557    
5558              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
5559              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
# Line 5146  sub _tree_construction_main ($) { Line 5604  sub _tree_construction_main ($) {
5604          !!!next-token;          !!!next-token;
5605          next B;          next B;
5606        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5607              if ({          if ({
5608                   tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => ($self->{insertion_mode} != IN_ROW_IM),
5609                   th => 1, td => 1,               th => 1, td => 1,
5610                  }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
5611                if ($self->{insertion_mode} == IN_TABLE_IM) {            if ($self->{insertion_mode} == IN_TABLE_IM) {
5612                  ## Clear back to table context              ## Clear back to table context
5613                  while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
5614                                  & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
5615                    !!!cp ('t201');                !!!cp ('t201');
5616                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5617                  }              }
5618                                
5619                  !!!insert-element ('tbody',, $token);              !!!insert-element ('tbody',, $token);
5620                  $self->{insertion_mode} = IN_TABLE_BODY_IM;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5621                  ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
5622                }            }
5623              
5624                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5625                  unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
5626                    !!!cp ('t202');                !!!cp ('t202');
5627                    !!!parse-error (type => 'missing start tag:tr', token => $token);                !!!parse-error (type => 'missing start tag:tr', token => $token);
5628                  }              }
5629                                    
5630                  ## Clear back to table body context              ## Clear back to table body context
5631                  while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
5632                                  & TABLE_ROWS_SCOPING_EL)) {                              & TABLE_ROWS_SCOPING_EL)) {
5633                    !!!cp ('t203');                !!!cp ('t203');
5634                    ## ISSUE: Can this case be reached?                ## ISSUE: Can this case be reached?
5635                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5636                  }              }
5637                                    
5638                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5639                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
# Line 5231  sub _tree_construction_main ($) { Line 5689  sub _tree_construction_main ($) {
5689                  unless (defined $i) {                  unless (defined $i) {
5690                    !!!cp ('t210');                    !!!cp ('t210');
5691  ## TODO: This type is wrong.  ## TODO: This type is wrong.
5692                    !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name}, token => $token);                    !!!parse-error (type => 'unmacthed end tag',
5693                                      text => $token->{tag_name}, token => $token);
5694                    ## Ignore the token                    ## Ignore the token
5695                    !!!nack ('t210.1');                    !!!nack ('t210.1');
5696                    !!!next-token;                    !!!next-token;
# Line 5275  sub _tree_construction_main ($) { Line 5734  sub _tree_construction_main ($) {
5734                  } # INSCOPE                  } # INSCOPE
5735                  unless (defined $i) {                  unless (defined $i) {
5736                    !!!cp ('t216');                    !!!cp ('t216');
5737  ## TODO: This erorr type ios wrong.  ## TODO: This erorr type is wrong.
5738                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                    !!!parse-error (type => 'unmatched end tag',
5739                                      text => $token->{tag_name}, token => $token);
5740                    ## Ignore the token                    ## Ignore the token
5741                    !!!nack ('t216.1');                    !!!nack ('t216.1');
5742                    !!!next-token;                    !!!next-token;
# Line 5351  sub _tree_construction_main ($) { Line 5811  sub _tree_construction_main ($) {
5811                }                }
5812              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5813                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
5814                                value => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
5815                                    ->manakai_local_name,                                    ->manakai_local_name,
5816                                token => $token);                                token => $token);
5817    
# Line 5372  sub _tree_construction_main ($) { Line 5832  sub _tree_construction_main ($) {
5832                unless (defined $i) {                unless (defined $i) {
5833                  !!!cp ('t223');                  !!!cp ('t223');
5834  ## TODO: The following is wrong, maybe.  ## TODO: The following is wrong, maybe.
5835                  !!!parse-error (type => 'unmatched end tag:table', token => $token);                  !!!parse-error (type => 'unmatched end tag', text => 'table',
5836                                    token => $token);
5837                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5838                  !!!nack ('t223.1');                  !!!nack ('t223.1');
5839                  !!!next-token;                  !!!next-token;
5840                  next B;                  next B;
5841                }                }
5842                                
5843  ## TODO: Followings are removed from the latest spec.  ## TODO: Followings are removed from the latest spec.
5844                ## generate implied end tags                ## generate implied end tags
5845                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5846                  !!!cp ('t224');                  !!!cp ('t224');
# Line 5390  sub _tree_construction_main ($) { Line 5851  sub _tree_construction_main ($) {
5851                  !!!cp ('t225');                  !!!cp ('t225');
5852                  ## NOTE: |<table><tr><table>|                  ## NOTE: |<table><tr><table>|
5853                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
5854                                  value => $self->{open_elements}->[-1]->[0]                                  text => $self->{open_elements}->[-1]->[0]
5855                                      ->manakai_local_name,                                      ->manakai_local_name,
5856                                  token => $token);                                  token => $token);
5857                } else {                } else {
# Line 5431  sub _tree_construction_main ($) { Line 5892  sub _tree_construction_main ($) {
5892                my $type = lc $token->{attributes}->{type}->{value};                my $type = lc $token->{attributes}->{type}->{value};
5893                if ($type eq 'hidden') {                if ($type eq 'hidden') {
5894                  !!!cp ('t227.3');                  !!!cp ('t227.3');
5895                  !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'in table',
5896                                    text => $token->{tag_name}, token => $token);
5897    
5898                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5899    
# Line 5459  sub _tree_construction_main ($) { Line 5921  sub _tree_construction_main ($) {
5921            #            #
5922          }          }
5923    
5924          !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);          !!!parse-error (type => 'in table', text => $token->{tag_name},
5925                            token => $token);
5926    
5927          $insert = $insert_to_foster;          $insert = $insert_to_foster;
5928          #          #
# Line 5481  sub _tree_construction_main ($) { Line 5944  sub _tree_construction_main ($) {
5944                } # INSCOPE                } # INSCOPE
5945                unless (defined $i) {                unless (defined $i) {
5946                  !!!cp ('t230');                  !!!cp ('t230');
5947                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5948                                    text => $token->{tag_name}, token => $token);
5949                  ## Ignore the token                  ## Ignore the token
5950                  !!!nack ('t230.1');                  !!!nack ('t230.1');
5951                  !!!next-token;                  !!!next-token;
# Line 5522  sub _tree_construction_main ($) { Line 5986  sub _tree_construction_main ($) {
5986                  unless (defined $i) {                  unless (defined $i) {
5987                    !!!cp ('t235');                    !!!cp ('t235');
5988  ## TODO: The following is wrong.  ## TODO: The following is wrong.
5989                    !!!parse-error (type => 'unmatched end tag:'.$token->{type}, token => $token);                    !!!parse-error (type => 'unmatched end tag',
5990                                      text => $token->{type}, token => $token);
5991                    ## Ignore the token                    ## Ignore the token
5992                    !!!nack ('t236.1');                    !!!nack ('t236.1');
5993                    !!!next-token;                    !!!next-token;
# Line 5558  sub _tree_construction_main ($) { Line 6023  sub _tree_construction_main ($) {
6023                  } # INSCOPE                  } # INSCOPE
6024                  unless (defined $i) {                  unless (defined $i) {
6025                    !!!cp ('t239');                    !!!cp ('t239');
6026                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                    !!!parse-error (type => 'unmatched end tag',
6027                                      text => $token->{tag_name}, token => $token);
6028                    ## Ignore the token                    ## Ignore the token
6029                    !!!nack ('t239.1');                    !!!nack ('t239.1');
6030                    !!!next-token;                    !!!next-token;
# Line 5604  sub _tree_construction_main ($) { Line 6070  sub _tree_construction_main ($) {
6070                } # INSCOPE                } # INSCOPE
6071                unless (defined $i) {                unless (defined $i) {
6072                  !!!cp ('t243');                  !!!cp ('t243');
6073                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
6074                                    text => $token->{tag_name}, token => $token);
6075                  ## Ignore the token                  ## Ignore the token
6076                  !!!nack ('t243.1');                  !!!nack ('t243.1');
6077                  !!!next-token;                  !!!next-token;
# Line 5638  sub _tree_construction_main ($) { Line 6105  sub _tree_construction_main ($) {
6105                  } # INSCOPE                  } # INSCOPE
6106                    unless (defined $i) {                    unless (defined $i) {
6107                      !!!cp ('t249');                      !!!cp ('t249');
6108                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                      !!!parse-error (type => 'unmatched end tag',
6109                                        text => $token->{tag_name}, token => $token);
6110                      ## Ignore the token                      ## Ignore the token
6111                      !!!nack ('t249.1');                      !!!nack ('t249.1');
6112                      !!!next-token;                      !!!next-token;
# Line 5661  sub _tree_construction_main ($) { Line 6129  sub _tree_construction_main ($) {
6129                  } # INSCOPE                  } # INSCOPE
6130                    unless (defined $i) {                    unless (defined $i) {
6131                      !!!cp ('t252');                      !!!cp ('t252');
6132                      !!!parse-error (type => 'unmatched end tag:tr', token => $token);                      !!!parse-error (type => 'unmatched end tag',
6133                                        text => 'tr', token => $token);
6134                      ## Ignore the token                      ## Ignore the token
6135                      !!!nack ('t252.1');                      !!!nack ('t252.1');
6136                      !!!next-token;                      !!!next-token;
# Line 5696  sub _tree_construction_main ($) { Line 6165  sub _tree_construction_main ($) {
6165                } # INSCOPE                } # INSCOPE
6166                unless (defined $i) {                unless (defined $i) {
6167                  !!!cp ('t256');                  !!!cp ('t256');
6168                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
6169                                    text => $token->{tag_name}, token => $token);
6170                  ## Ignore the token                  ## Ignore the token
6171                  !!!nack ('t256.1');                  !!!nack ('t256.1');
6172                  !!!next-token;                  !!!next-token;
# Line 5723  sub _tree_construction_main ($) { Line 6193  sub _tree_construction_main ($) {
6193                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6194                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6195            !!!cp ('t258');            !!!cp ('t258');
6196            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
6197                              text => $token->{tag_name}, token => $token);
6198            ## Ignore the token            ## Ignore the token
6199            !!!nack ('t258.1');            !!!nack ('t258.1');
6200             !!!next-token;             !!!next-token;
6201            next B;            next B;
6202          } else {          } else {
6203            !!!cp ('t259');            !!!cp ('t259');
6204            !!!parse-error (type => 'in table:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'in table:/',
6205                              text => $token->{tag_name}, token => $token);
6206    
6207            $insert = $insert_to_foster;            $insert = $insert_to_foster;
6208            #            #
# Line 5753  sub _tree_construction_main ($) { Line 6225  sub _tree_construction_main ($) {
6225        }        }
6226      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6227            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6228              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6229                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6230                unless (length $token->{data}) {                unless (length $token->{data}) {
6231                  !!!cp ('t260');                  !!!cp ('t260');
# Line 5780  sub _tree_construction_main ($) { Line 6252  sub _tree_construction_main ($) {
6252              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
6253                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6254                  !!!cp ('t264');                  !!!cp ('t264');
6255                  !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);                  !!!parse-error (type => 'unmatched end tag',
6256                                    text => 'colgroup', token => $token);
6257                  ## Ignore the token                  ## Ignore the token
6258                  !!!next-token;                  !!!next-token;
6259                  next B;                  next B;
# Line 5793  sub _tree_construction_main ($) { Line 6266  sub _tree_construction_main ($) {
6266                }                }
6267              } elsif ($token->{tag_name} eq 'col') {              } elsif ($token->{tag_name} eq 'col') {
6268                !!!cp ('t266');                !!!cp ('t266');
6269                !!!parse-error (type => 'unmatched end tag:col', token => $token);                !!!parse-error (type => 'unmatched end tag',
6270                                  text => 'col', token => $token);
6271                ## Ignore the token                ## Ignore the token
6272                !!!next-token;                !!!next-token;
6273                next B;                next B;
# Line 5823  sub _tree_construction_main ($) { Line 6297  sub _tree_construction_main ($) {
6297            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6298              !!!cp ('t269');              !!!cp ('t269');
6299  ## TODO: Wrong error type?  ## TODO: Wrong error type?
6300              !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);              !!!parse-error (type => 'unmatched end tag',
6301                                text => 'colgroup', token => $token);
6302              ## Ignore the token              ## Ignore the token
6303              !!!nack ('t269.1');              !!!nack ('t269.1');
6304              !!!next-token;              !!!next-token;
# Line 5877  sub _tree_construction_main ($) { Line 6352  sub _tree_construction_main ($) {
6352            !!!nack ('t277.1');            !!!nack ('t277.1');
6353            !!!next-token;            !!!next-token;
6354            next B;            next B;
6355          } elsif ($token->{tag_name} eq 'select' or          } elsif ({
6356                   $token->{tag_name} eq 'input' or                     select => 1, input => 1, textarea => 1,
6357                     }->{$token->{tag_name}} or
6358                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6359                    {                    {
6360                     caption => 1, table => 1,                     caption => 1, table => 1,
# Line 5886  sub _tree_construction_main ($) { Line 6362  sub _tree_construction_main ($) {
6362                     tr => 1, td => 1, th => 1,                     tr => 1, td => 1, th => 1,
6363                    }->{$token->{tag_name}})) {                    }->{$token->{tag_name}})) {
6364            ## TODO: The type below is not good - <select> is replaced by </select>            ## TODO: The type below is not good - <select> is replaced by </select>
6365            !!!parse-error (type => 'not closed:select', token => $token);            !!!parse-error (type => 'not closed', text => 'select',
6366                              token => $token);
6367            ## NOTE: As if the token were </select> (<select> case) or            ## NOTE: As if the token were </select> (<select> case) or
6368            ## as if there were </select> (otherwise).            ## as if there were </select> (otherwise).
6369            ## have an element in table scope            ## have an element in table scope
# Line 5904  sub _tree_construction_main ($) { Line 6381  sub _tree_construction_main ($) {
6381            } # INSCOPE            } # INSCOPE
6382            unless (defined $i) {            unless (defined $i) {
6383              !!!cp ('t280');              !!!cp ('t280');
6384              !!!parse-error (type => 'unmatched end tag:select', token => $token);              !!!parse-error (type => 'unmatched end tag',
6385                                text => 'select', token => $token);
6386              ## Ignore the token              ## Ignore the token
6387              !!!nack ('t280.1');              !!!nack ('t280.1');
6388              !!!next-token;              !!!next-token;
# Line 5928  sub _tree_construction_main ($) { Line 6406  sub _tree_construction_main ($) {
6406            }            }
6407          } else {          } else {
6408            !!!cp ('t282');            !!!cp ('t282');
6409            !!!parse-error (type => 'in select:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'in select',
6410                              text => $token->{tag_name}, token => $token);
6411            ## Ignore the token            ## Ignore the token
6412            !!!nack ('t282.1');            !!!nack ('t282.1');
6413            !!!next-token;            !!!next-token;
# Line 5946  sub _tree_construction_main ($) { Line 6425  sub _tree_construction_main ($) {
6425              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6426            } else {            } else {
6427              !!!cp ('t285');              !!!cp ('t285');
6428              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
6429                                text => $token->{tag_name}, token => $token);
6430              ## Ignore the token              ## Ignore the token
6431            }            }
6432            !!!nack ('t285.1');            !!!nack ('t285.1');
# Line 5958  sub _tree_construction_main ($) { Line 6438  sub _tree_construction_main ($) {
6438              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6439            } else {            } else {
6440              !!!cp ('t287');              !!!cp ('t287');
6441              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
6442                                text => $token->{tag_name}, token => $token);
6443              ## Ignore the token              ## Ignore the token
6444            }            }
6445            !!!nack ('t287.1');            !!!nack ('t287.1');
# Line 5980  sub _tree_construction_main ($) { Line 6461  sub _tree_construction_main ($) {
6461            } # INSCOPE            } # INSCOPE
6462            unless (defined $i) {            unless (defined $i) {
6463              !!!cp ('t290');              !!!cp ('t290');
6464              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
6465                                text => $token->{tag_name}, token => $token);
6466              ## Ignore the token              ## Ignore the token
6467              !!!nack ('t290.1');              !!!nack ('t290.1');
6468              !!!next-token;              !!!next-token;
# Line 6001  sub _tree_construction_main ($) { Line 6483  sub _tree_construction_main ($) {
6483                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6484                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
6485  ## TODO: The following is wrong?  ## TODO: The following is wrong?
6486            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
6487                              text => $token->{tag_name}, token => $token);
6488                                
6489            ## have an element in table scope            ## have an element in table scope
6490            my $i;            my $i;
# Line 6042  sub _tree_construction_main ($) { Line 6525  sub _tree_construction_main ($) {
6525            unless (defined $i) {            unless (defined $i) {
6526              !!!cp ('t297');              !!!cp ('t297');
6527  ## TODO: The following error type is correct?  ## TODO: The following error type is correct?
6528              !!!parse-error (type => 'unmatched end tag:select', token => $token);              !!!parse-error (type => 'unmatched end tag',
6529                                text => 'select', token => $token);
6530              ## Ignore the </select> token              ## Ignore the </select> token
6531              !!!nack ('t297.1');              !!!nack ('t297.1');
6532              !!!next-token; ## TODO: ok?              !!!next-token; ## TODO: ok?
# Line 6059  sub _tree_construction_main ($) { Line 6543  sub _tree_construction_main ($) {
6543            next B;            next B;
6544          } else {          } else {
6545            !!!cp ('t299');            !!!cp ('t299');
6546            !!!parse-error (type => 'in select:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'in select:/',
6547                              text => $token->{tag_name}, token => $token);
6548            ## Ignore the token            ## Ignore the token
6549            !!!nack ('t299.3');            !!!nack ('t299.3');
6550            !!!next-token;            !!!next-token;
# Line 6081  sub _tree_construction_main ($) { Line 6566  sub _tree_construction_main ($) {
6566        }        }
6567      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6568        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6569          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6570            my $data = $1;            my $data = $1;
6571            ## As if in body            ## As if in body
6572            $reconstruct_active_formatting_elements->($insert_to_current);            $reconstruct_active_formatting_elements->($insert_to_current);
# Line 6097  sub _tree_construction_main ($) { Line 6582  sub _tree_construction_main ($) {
6582                    
6583          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6584            !!!cp ('t301');            !!!cp ('t301');
6585            !!!parse-error (type => 'after html:#character', token => $token);            !!!parse-error (type => 'after html:#text', token => $token);
6586              #
           ## Reprocess in the "after body" insertion mode.  
6587          } else {          } else {
6588            !!!cp ('t302');            !!!cp ('t302');
6589              ## "after body" insertion mode
6590              !!!parse-error (type => 'after body:#text', token => $token);
6591              #
6592          }          }
           
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:#character', token => $token);  
6593    
6594          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6595          ## reprocess          ## reprocess
# Line 6113  sub _tree_construction_main ($) { Line 6597  sub _tree_construction_main ($) {
6597        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6598          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6599            !!!cp ('t303');            !!!cp ('t303');
6600            !!!parse-error (type => 'after html:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'after html',
6601                                        text => $token->{tag_name}, token => $token);
6602            ## Reprocess in the "after body" insertion mode.            #
6603          } else {          } else {
6604            !!!cp ('t304');            !!!cp ('t304');
6605              ## "after body" insertion mode
6606              !!!parse-error (type => 'after body',
6607                              text => $token->{tag_name}, token => $token);
6608              #
6609          }          }
6610    
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:'.$token->{tag_name}, token => $token);  
   
6611          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6612          !!!ack-later;          !!!ack-later;
6613          ## reprocess          ## reprocess
# Line 6130  sub _tree_construction_main ($) { Line 6615  sub _tree_construction_main ($) {
6615        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6616          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6617            !!!cp ('t305');            !!!cp ('t305');
6618            !!!parse-error (type => 'after html:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'after html:/',
6619                              text => $token->{tag_name}, token => $token);
6620                        
6621            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6622            ## Reprocess in the "after body" insertion mode.            ## Reprocess.
6623              next B;
6624          } else {          } else {
6625            !!!cp ('t306');            !!!cp ('t306');
6626          }          }
# Line 6142  sub _tree_construction_main ($) { Line 6629  sub _tree_construction_main ($) {
6629          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
6630            if (defined $self->{inner_html_node}) {            if (defined $self->{inner_html_node}) {
6631              !!!cp ('t307');              !!!cp ('t307');
6632              !!!parse-error (type => 'unmatched end tag:html', token => $token);              !!!parse-error (type => 'unmatched end tag',
6633                                text => 'html', token => $token);
6634              ## Ignore the token              ## Ignore the token
6635              !!!next-token;              !!!next-token;
6636              next B;              next B;
# Line 6154  sub _tree_construction_main ($) { Line 6642  sub _tree_construction_main ($) {
6642            }            }
6643          } else {          } else {
6644            !!!cp ('t309');            !!!cp ('t309');
6645            !!!parse-error (type => 'after body:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'after body:/',
6646                              text => $token->{tag_name}, token => $token);
6647    
6648            $self->{insertion_mode} = IN_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6649            ## reprocess            ## reprocess
# Line 6169  sub _tree_construction_main ($) { Line 6658  sub _tree_construction_main ($) {
6658        }        }
6659      } elsif ($self->{insertion_mode} & FRAME_IMS) {      } elsif ($self->{insertion_mode} & FRAME_IMS) {
6660        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6661          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6662            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6663                        
6664            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 6179  sub _tree_construction_main ($) { Line 6668  sub _tree_construction_main ($) {
6668            }            }
6669          }          }
6670                    
6671          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6672            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6673              !!!cp ('t311');              !!!cp ('t311');
6674              !!!parse-error (type => 'in frameset:#character', token => $token);              !!!parse-error (type => 'in frameset:#text', token => $token);
6675            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6676              !!!cp ('t312');              !!!cp ('t312');
6677              !!!parse-error (type => 'after frameset:#character', token => $token);              !!!parse-error (type => 'after frameset:#text', token => $token);
6678            } else { # "after html frameset"            } else { # "after after frameset"
6679              !!!cp ('t313');              !!!cp ('t313');
6680              !!!parse-error (type => 'after html:#character', token => $token);              !!!parse-error (type => 'after html:#text', token => $token);
   
             $self->{insertion_mode} = AFTER_FRAMESET_IM;  
             ## Reprocess in the "after frameset" insertion mode.  
             !!!parse-error (type => 'after frameset:#character', token => $token);  
6681            }            }
6682                        
6683            ## Ignore the token.            ## Ignore the token.
# Line 6208  sub _tree_construction_main ($) { Line 6693  sub _tree_construction_main ($) {
6693                    
6694          die qq[$0: Character "$token->{data}"];          die qq[$0: Character "$token->{data}"];
6695        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
         if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {  
           !!!cp ('t316');  
           !!!parse-error (type => 'after html:'.$token->{tag_name}, token => $token);  
   
           $self->{insertion_mode} = AFTER_FRAMESET_IM;  
           ## Process in the "after frameset" insertion mode.  
         } else {  
           !!!cp ('t317');  
         }  
   
6696          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6697              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6698            !!!cp ('t318');            !!!cp ('t318');
# Line 6235  sub _tree_construction_main ($) { Line 6710  sub _tree_construction_main ($) {
6710            next B;            next B;
6711          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
6712            !!!cp ('t320');            !!!cp ('t320');
6713            ## NOTE: As if in body.            ## NOTE: As if in head.
6714            $parse_rcdata->(CDATA_CONTENT_MODEL);            $parse_rcdata->(CDATA_CONTENT_MODEL);
6715            next B;            next B;
6716    
6717              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6718              ## has no parse error.
6719          } else {          } else {
6720            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6721              !!!cp ('t321');              !!!cp ('t321');
6722              !!!parse-error (type => 'in frameset:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'in frameset',
6723            } else {                              text => $token->{tag_name}, token => $token);
6724              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6725              !!!cp ('t322');              !!!cp ('t322');
6726              !!!parse-error (type => 'after frameset:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'after frameset',
6727                                text => $token->{tag_name}, token => $token);
6728              } else { # "after after frameset"
6729                !!!cp ('t322.2');
6730                !!!parse-error (type => 'after after frameset',
6731                                text => $token->{tag_name}, token => $token);
6732            }            }
6733            ## Ignore the token            ## Ignore the token
6734            !!!nack ('t322.1');            !!!nack ('t322.1');
# Line 6252  sub _tree_construction_main ($) { Line 6736  sub _tree_construction_main ($) {
6736            next B;            next B;
6737          }          }
6738        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
         if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {  
           !!!cp ('t323');  
           !!!parse-error (type => 'after html:/'.$token->{tag_name}, token => $token);  
   
           $self->{insertion_mode} = AFTER_FRAMESET_IM;  
           ## Process in the "after frameset" insertion mode.  
         } else {  
           !!!cp ('t324');  
         }  
   
6739          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6740              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6741            if ($self->{open_elements}->[-1]->[1] & HTML_EL and            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6742                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
6743              !!!cp ('t325');              !!!cp ('t325');
6744              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
6745                                text => $token->{tag_name}, token => $token);
6746              ## Ignore the token              ## Ignore the token
6747              !!!next-token;              !!!next-token;
6748            } else {            } else {
# Line 6293  sub _tree_construction_main ($) { Line 6768  sub _tree_construction_main ($) {
6768          } else {          } else {
6769            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6770              !!!cp ('t330');              !!!cp ('t330');
6771              !!!parse-error (type => 'in frameset:/'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'in frameset:/',
6772            } else {                              text => $token->{tag_name}, token => $token);
6773              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6774                !!!cp ('t330.1');
6775                !!!parse-error (type => 'after frameset:/',
6776                                text => $token->{tag_name}, token => $token);
6777              } else { # "after after html"
6778              !!!cp ('t331');              !!!cp ('t331');
6779              !!!parse-error (type => 'after frameset:/'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'after after frameset:/',
6780                                text => $token->{tag_name}, token => $token);
6781            }            }
6782            ## Ignore the token            ## Ignore the token
6783            !!!next-token;            !!!next-token;
# Line 6316  sub _tree_construction_main ($) { Line 6797  sub _tree_construction_main ($) {
6797        } else {        } else {
6798          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6799        }        }
   
       ## ISSUE: An issue in spec here  
6800      } else {      } else {
6801        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
6802      }      }
# Line 6335  sub _tree_construction_main ($) { Line 6814  sub _tree_construction_main ($) {
6814          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
6815          next B;          next B;
6816        } elsif ({        } elsif ({
6817                  base => 1, link => 1,                  base => 1, command => 1, eventsource => 1, link => 1,
6818                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6819          !!!cp ('t334');          !!!cp ('t334');
6820          ## 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
6821          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6822          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}};
6823          !!!ack ('t334.1');          !!!ack ('t334.1');
6824          !!!next-token;          !!!next-token;
6825          next B;          next B;
6826        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
6827          ## 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
6828          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6829          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          my $meta_el = pop @{$self->{open_elements}};
6830    
6831          unless ($self->{confident}) {          unless ($self->{confident}) {
6832            if ($token->{attributes}->{charset}) {            if ($token->{attributes}->{charset}) {
# Line 6363  sub _tree_construction_main ($) { Line 6842  sub _tree_construction_main ($) {
6842                                           ->{has_reference});                                           ->{has_reference});
6843            } elsif ($token->{attributes}->{content}) {            } elsif ($token->{attributes}->{content}) {
6844              if ($token->{attributes}->{content}->{value}              if ($token->{attributes}->{content}->{value}
6845                  =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6846                      [\x09-\x0D\x20]*=                      [\x09\x0A\x0C\x0D\x20]*=
6847                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                      [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6848                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                      ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6849                       /x) {
6850                !!!cp ('t336');                !!!cp ('t336');
6851                ## NOTE: Whether the encoding is supported or not is handled                ## NOTE: Whether the encoding is supported or not is handled
6852                ## in the {change_encoding} callback.                ## in the {change_encoding} callback.
# Line 6404  sub _tree_construction_main ($) { Line 6884  sub _tree_construction_main ($) {
6884          $parse_rcdata->(RCDATA_CONTENT_MODEL);          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6885          next B;          next B;
6886        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
6887          !!!parse-error (type => 'in body:body', token => $token);          !!!parse-error (type => 'in body', text => 'body', token => $token);
6888                                
6889          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
6890              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
# Line 6425  sub _tree_construction_main ($) { Line 6905  sub _tree_construction_main ($) {
6905          !!!next-token;          !!!next-token;
6906          next B;          next B;
6907        } elsif ({        } elsif ({
6908                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: Start tags for non-phrasing flow content elements
6909                  div => 1, dl => 1, fieldset => 1,  
6910                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  ## NOTE: The normal one
6911                  menu => 1, ol => 1, p => 1, ul => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
6912                    center => 1, datagrid => 1, details => 1, dialog => 1,
6913                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
6914                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
6915                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
6916                    section => 1, ul => 1,
6917                    ## NOTE: As normal, but drops leading newline
6918                  pre => 1, listing => 1,                  pre => 1, listing => 1,
6919                    ## NOTE: As normal, but interacts with the form element pointer
6920                  form => 1,                  form => 1,
6921                    
6922                  table => 1,                  table => 1,
6923                  hr => 1,                  hr => 1,
6924                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
# Line 6497  sub _tree_construction_main ($) { Line 6985  sub _tree_construction_main ($) {
6985            !!!next-token;            !!!next-token;
6986          }          }
6987          next B;          next B;
6988        } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {        } elsif ($token->{tag_name} eq 'li') {
6989          ## has a p element in scope          ## NOTE: As normal, but imply </li> when there's another <li> ...
6990    
6991            ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
6992              ## Interpreted as <li><foo/></li><li/> (non-conforming)
6993              ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
6994              ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
6995              ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
6996              ## object (Fx)
6997              ## Generate non-tree (non-conforming)
6998              ## basefont (IE7 (where basefont is non-void)), center (IE),
6999              ## form (IE), hn (IE)
7000            ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7001              ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7002              ## div (Fx, S)
7003    
7004            my $non_optional;
7005            my $i = -1;
7006    
7007            ## 1.
7008            for my $node (reverse @{$self->{open_elements}}) {
7009              if ($node->[1] & LI_EL) {
7010                ## 2. (a) As if </li>
7011                {
7012                  ## If no </li> - not applied
7013                  #
7014    
7015                  ## Otherwise
7016    
7017                  ## 1. generate implied end tags, except for </li>
7018                  #
7019    
7020                  ## 2. If current node != "li", parse error
7021                  if ($non_optional) {
7022                    !!!parse-error (type => 'not closed',
7023                                    text => $non_optional->[0]->manakai_local_name,
7024                                    token => $token);
7025                    !!!cp ('t355');
7026                  } else {
7027                    !!!cp ('t356');
7028                  }
7029    
7030                  ## 3. Pop
7031                  splice @{$self->{open_elements}}, $i;
7032                }
7033    
7034                last; ## 2. (b) goto 5.
7035              } elsif (
7036                       ## NOTE: not "formatting" and not "phrasing"
7037                       ($node->[1] & SPECIAL_EL or
7038                        $node->[1] & SCOPING_EL) and
7039                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7040    
7041                       (not $node->[1] & ADDRESS_EL) &
7042                       (not $node->[1] & DIV_EL) &
7043                       (not $node->[1] & P_EL)) {
7044                ## 3.
7045                !!!cp ('t357');
7046                last; ## goto 5.
7047              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7048                !!!cp ('t358');
7049                #
7050              } else {
7051                !!!cp ('t359');
7052                $non_optional ||= $node;
7053                #
7054              }
7055              ## 4.
7056              ## goto 2.
7057              $i--;
7058            }
7059    
7060            ## 5. (a) has a |p| element in scope
7061          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7062            if ($_->[1] & P_EL) {            if ($_->[1] & P_EL) {
7063              !!!cp ('t353');              !!!cp ('t353');
7064    
7065                ## NOTE: |<p><li>|, for example.
7066    
7067              !!!back-token; # <x>              !!!back-token; # <x>
7068              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7069                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
# Line 6511  sub _tree_construction_main ($) { Line 7073  sub _tree_construction_main ($) {
7073              last INSCOPE;              last INSCOPE;
7074            }            }
7075          } # INSCOPE          } # INSCOPE
7076              
7077          ## Step 1          ## 5. (b) insert
7078            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7079            !!!nack ('t359.1');
7080            !!!next-token;
7081            next B;
7082          } elsif ($token->{tag_name} eq 'dt' or
7083                   $token->{tag_name} eq 'dd') {
7084            ## NOTE: As normal, but imply </dt> or </dd> when ...
7085    
7086            my $non_optional;
7087          my $i = -1;          my $i = -1;
7088          my $node = $self->{open_elements}->[$i];  
7089          my $li_or_dtdd = {li => {li => 1},          ## 1.
7090                            dt => {dt => 1, dd => 1},          for my $node (reverse @{$self->{open_elements}}) {
7091                            dd => {dt => 1, dd => 1}}->{$token->{tag_name}};            if ($node->[1] & DT_EL or $node->[1] & DD_EL) {
7092          LI: {              ## 2. (a) As if </li>
7093            ## Step 2              {
7094            if ($li_or_dtdd->{$node->[0]->manakai_local_name}) {                ## If no </li> - not applied
7095              if ($i != -1) {                #
7096                !!!cp ('t355');  
7097                !!!parse-error (type => 'not closed',                ## Otherwise
7098                                value => $self->{open_elements}->[-1]->[0]  
7099                                    ->manakai_local_name,                ## 1. generate implied end tags, except for </dt> or </dd>
7100                                token => $token);                #
7101              } else {  
7102                !!!cp ('t356');                ## 2. If current node != "dt"|"dd", parse error
7103                  if ($non_optional) {
7104                    !!!parse-error (type => 'not closed',
7105                                    text => $non_optional->[0]->manakai_local_name,
7106                                    token => $token);
7107                    !!!cp ('t355.1');
7108                  } else {
7109                    !!!cp ('t356.1');
7110                  }
7111    
7112                  ## 3. Pop
7113                  splice @{$self->{open_elements}}, $i;
7114              }              }
7115              splice @{$self->{open_elements}}, $i;  
7116              last LI;              last; ## 2. (b) goto 5.
7117              } elsif (
7118                       ## NOTE: not "formatting" and not "phrasing"
7119                       ($node->[1] & SPECIAL_EL or
7120                        $node->[1] & SCOPING_EL) and
7121                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7122    
7123                       (not $node->[1] & ADDRESS_EL) &
7124                       (not $node->[1] & DIV_EL) &
7125                       (not $node->[1] & P_EL)) {
7126                ## 3.
7127                !!!cp ('t357.1');
7128                last; ## goto 5.
7129              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7130                !!!cp ('t358.1');
7131                #
7132            } else {            } else {
7133              !!!cp ('t357');              !!!cp ('t359.1');
7134            }              $non_optional ||= $node;
7135                          #
           ## 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;  
7136            }            }
7137                        ## 4.
7138            !!!cp ('t359');            ## goto 2.
           ## Step 4  
7139            $i--;            $i--;
7140            $node = $self->{open_elements}->[$i];          }
7141            redo LI;  
7142          } # LI          ## 5. (a) has a |p| element in scope
7143                      INSCOPE: for (reverse @{$self->{open_elements}}) {
7144              if ($_->[1] & P_EL) {
7145                !!!cp ('t353.1');
7146                !!!back-token; # <x>
7147                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7148                          line => $token->{line}, column => $token->{column}};
7149                next B;
7150              } elsif ($_->[1] & SCOPING_EL) {
7151                !!!cp ('t354.1');
7152                last INSCOPE;
7153              }
7154            } # INSCOPE
7155    
7156            ## 5. (b) insert
7157          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7158          !!!nack ('t359.1');          !!!nack ('t359.2');
7159          !!!next-token;          !!!next-token;
7160          next B;          next B;
7161        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
7162            ## NOTE: As normal, but effectively ends parsing
7163    
7164          ## has a p element in scope          ## has a p element in scope
7165          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7166            if ($_->[1] & P_EL) {            if ($_->[1] & P_EL) {
# Line 6678  sub _tree_construction_main ($) { Line 7280  sub _tree_construction_main ($) {
7280                  xmp => 1,                  xmp => 1,
7281                  iframe => 1,                  iframe => 1,
7282                  noembed => 1,                  noembed => 1,
7283                  noframes => 1,                  noframes => 1, ## NOTE: This is an "as if in head" code clone.
7284                  noscript => 0, ## TODO: 1 if scripting is enabled                  noscript => 0, ## TODO: 1 if scripting is enabled
7285                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7286          if ($token->{tag_name} eq 'xmp') {          if ($token->{tag_name} eq 'xmp') {
# Line 6700  sub _tree_construction_main ($) { Line 7302  sub _tree_construction_main ($) {
7302            !!!next-token;            !!!next-token;
7303            next B;            next B;
7304          } else {          } else {
7305              !!!ack ('t391.1');
7306    
7307            my $at = $token->{attributes};            my $at = $token->{attributes};
7308            my $form_attrs;            my $form_attrs;
7309            $form_attrs->{action} = $at->{action} if $at->{action};            $form_attrs->{action} = $at->{action} if $at->{action};
# Line 6743  sub _tree_construction_main ($) { Line 7347  sub _tree_construction_main ($) {
7347                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
7348                          {type => END_TAG_TOKEN, tag_name => 'form',                          {type => END_TAG_TOKEN, tag_name => 'form',
7349                           line => $token->{line}, column => $token->{column}};                           line => $token->{line}, column => $token->{column}};
           !!!nack ('t391.1'); ## NOTE: Not acknowledged.  
7350            !!!back-token (@tokens);            !!!back-token (@tokens);
7351            !!!next-token;            !!!next-token;
7352            next B;            next B;
# Line 6791  sub _tree_construction_main ($) { Line 7394  sub _tree_construction_main ($) {
7394            ## Ignore the token            ## Ignore the token
7395          } else {          } else {
7396            !!!cp ('t398');            !!!cp ('t398');
7397            !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token);            !!!parse-error (type => 'in RCDATA:#eof', token => $token);
7398          }          }
7399          !!!next-token;          !!!next-token;
7400          next B;          next B;
7401          } elsif ($token->{tag_name} eq 'optgroup' or
7402                   $token->{tag_name} eq 'option') {
7403            ## has an |option| element in scope
7404            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7405              my $node = $self->{open_elements}->[$_];
7406              if ($node->[1] & OPTION_EL) {
7407                !!!cp ('t397.1');
7408                ## NOTE: As if </option>
7409                !!!back-token; # <option> or <optgroup>
7410                $token = {type => END_TAG_TOKEN, tag_name => 'option',
7411                          line => $token->{line}, column => $token->{column}};
7412                next B;
7413              } elsif ($node->[1] & SCOPING_EL) {
7414                !!!cp ('t397.2');
7415                last INSCOPE;
7416              }
7417            } # INSCOPE
7418    
7419            $reconstruct_active_formatting_elements->($insert_to_current);
7420    
7421            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7422    
7423            !!!nack ('t397.3');
7424            !!!next-token;
7425            redo B;
7426          } elsif ($token->{tag_name} eq 'rt' or
7427                   $token->{tag_name} eq 'rp') {
7428            ## has a |ruby| element in scope
7429            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7430              my $node = $self->{open_elements}->[$_];
7431              if ($node->[1] & RUBY_EL) {
7432                !!!cp ('t398.1');
7433                ## generate implied end tags
7434                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7435                  !!!cp ('t398.2');
7436                  pop @{$self->{open_elements}};
7437                }
7438                unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7439                  !!!cp ('t398.3');
7440                  !!!parse-error (type => 'not closed',
7441                                  text => $self->{open_elements}->[-1]->[0]
7442                                      ->manakai_local_name,
7443                                  token => $token);
7444                  pop @{$self->{open_elements}}
7445                      while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
7446                }
7447                last INSCOPE;
7448              } elsif ($node->[1] & SCOPING_EL) {
7449                !!!cp ('t398.4');
7450                last INSCOPE;
7451              }
7452            } # INSCOPE
7453    
7454            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7455    
7456            !!!nack ('t398.5');
7457            !!!next-token;
7458            redo B;
7459        } elsif ($token->{tag_name} eq 'math' or        } elsif ($token->{tag_name} eq 'math' or
7460                 $token->{tag_name} eq 'svg') {                 $token->{tag_name} eq 'svg') {
7461          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7462    
7463            ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7464    
7465          ## "adjust SVG attributes" ('svg' only) - done in insert-element-f          ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7466    
7467          ## "adjust foreign attributes" - done in insert-element-f          ## "adjust foreign attributes" - done in insert-element-f
# Line 6807  sub _tree_construction_main ($) { Line 7470  sub _tree_construction_main ($) {
7470                    
7471          if ($self->{self_closing}) {          if ($self->{self_closing}) {
7472            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
7473            !!!ack ('t398.1');            !!!ack ('t398.6');
7474          } else {          } else {
7475            !!!cp ('t398.2');            !!!cp ('t398.7');
7476            $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;            $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7477            ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion            ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7478            ## mode, "in body" (not "in foreign content") secondary insertion            ## mode, "in body" (not "in foreign content") secondary insertion
# Line 6820  sub _tree_construction_main ($) { Line 7483  sub _tree_construction_main ($) {
7483          next B;          next B;
7484        } elsif ({        } elsif ({
7485                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
7486                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1,
7487                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
7488                  thead => 1, tr => 1,                  thead => 1, tr => 1,
7489                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7490          !!!cp ('t401');          !!!cp ('t401');
7491          !!!parse-error (type => 'in body:'.$token->{tag_name}, token => $token);          !!!parse-error (type => 'in body',
7492                            text => $token->{tag_name}, token => $token);
7493          ## Ignore the token          ## Ignore the token
7494          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7495          !!!next-token;          !!!next-token;
7496          next B;          next B;
7497                  } elsif ($token->{tag_name} eq 'param' or
7498          ## ISSUE: An issue on HTML5 new elements in the spec.                 $token->{tag_name} eq 'source') {
7499            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7500            pop @{$self->{open_elements}};
7501    
7502            !!!ack ('t398.5');
7503            !!!next-token;
7504            redo B;
7505        } else {        } else {
7506          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'image') {
7507            !!!cp ('t384');            !!!cp ('t384');
# Line 6854  sub _tree_construction_main ($) { Line 7524  sub _tree_construction_main ($) {
7524            !!!nack ('t380.1');            !!!nack ('t380.1');
7525          } elsif ({          } elsif ({
7526                    b => 1, big => 1, em => 1, font => 1, i => 1,                    b => 1, big => 1, em => 1, font => 1, i => 1,
7527                    s => 1, small => 1, strile => 1,                    s => 1, small => 1, strike => 1,
7528                    strong => 1, tt => 1, u => 1,                    strong => 1, tt => 1, u => 1,
7529                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
7530            !!!cp ('t375');            !!!cp ('t375');
# Line 6867  sub _tree_construction_main ($) { Line 7537  sub _tree_construction_main ($) {
7537            !!!ack ('t388.2');            !!!ack ('t388.2');
7538          } elsif ({          } elsif ({
7539                    area => 1, basefont => 1, bgsound => 1, br => 1,                    area => 1, basefont => 1, bgsound => 1, br => 1,
7540                    embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                    embed => 1, img => 1, spacer => 1, wbr => 1,
                   #image => 1,  
7541                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
7542            !!!cp ('t388.1');            !!!cp ('t388.1');
7543            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
# Line 6909  sub _tree_construction_main ($) { Line 7578  sub _tree_construction_main ($) {
7578              }              }
7579            }            }
7580    
7581            !!!parse-error (type => 'start tag not allowed',            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
7582                            value => $token->{tag_name}, token => $token);  
7583              !!!parse-error (type => 'unmatched end tag',
7584                              text => $token->{tag_name}, token => $token);
7585            ## NOTE: Ignore the token.            ## NOTE: Ignore the token.
7586            !!!next-token;            !!!next-token;
7587            next B;            next B;
# Line 6920  sub _tree_construction_main ($) { Line 7591  sub _tree_construction_main ($) {
7591            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7592              !!!cp ('t403');              !!!cp ('t403');
7593              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
7594                              value => $_->[0]->manakai_local_name,                              text => $_->[0]->manakai_local_name,
7595                              token => $token);                              token => $token);
7596              last;              last;
7597            } else {            } else {
# Line 6936  sub _tree_construction_main ($) { Line 7607  sub _tree_construction_main ($) {
7607          ## up-to-date, though it has same effect as speced.          ## up-to-date, though it has same effect as speced.
7608          if (@{$self->{open_elements}} > 1 and          if (@{$self->{open_elements}} > 1 and
7609              $self->{open_elements}->[1]->[1] & BODY_EL) {              $self->{open_elements}->[1]->[1] & BODY_EL) {
           ## ISSUE: There is an issue in the spec.  
7610            unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {            unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7611              !!!cp ('t406');              !!!cp ('t406');
7612              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
7613                              value => $self->{open_elements}->[1]->[0]                              text => $self->{open_elements}->[1]->[0]
7614                                  ->manakai_local_name,                                  ->manakai_local_name,
7615                              token => $token);                              token => $token);
7616            } else {            } else {
# Line 6951  sub _tree_construction_main ($) { Line 7621  sub _tree_construction_main ($) {
7621            next B;            next B;
7622          } else {          } else {
7623            !!!cp ('t408');            !!!cp ('t408');
7624            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7625                              text => $token->{tag_name}, token => $token);
7626            ## Ignore the token            ## Ignore the token
7627            !!!next-token;            !!!next-token;
7628            next B;            next B;
7629          }          }
7630        } elsif ({        } elsif ({
7631                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: End tags for non-phrasing flow content elements
7632                  div => 1, dl => 1, fieldset => 1, listing => 1,  
7633                  menu => 1, ol => 1, pre => 1, ul => 1,                  ## NOTE: The normal ones
7634                    address => 1, article => 1, aside => 1, blockquote => 1,
7635                    center => 1, datagrid => 1, details => 1, dialog => 1,
7636                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
7637                    footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
7638                    ol => 1, pre => 1, section => 1, ul => 1,
7639    
7640                    ## NOTE: As normal, but ... optional tags
7641                  dd => 1, dt => 1, li => 1,                  dd => 1, dt => 1, li => 1,
7642    
7643                  applet => 1, button => 1, marquee => 1, object => 1,                  applet => 1, button => 1, marquee => 1, object => 1,
7644                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7645            ## NOTE: Code for <li> start tags includes "as if </li>" code.
7646            ## Code for <dt> or <dd> start tags includes "as if </dt> or
7647            ## </dd>" code.
7648    
7649          ## has an element in scope          ## has an element in scope
7650          my $i;          my $i;
7651          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 6979  sub _tree_construction_main ($) { Line 7662  sub _tree_construction_main ($) {
7662    
7663          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7664            !!!cp ('t413');            !!!cp ('t413');
7665            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7666                              text => $token->{tag_name}, token => $token);
7667              ## NOTE: Ignore the token.
7668          } else {          } else {
7669            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7670            while ({            while ({
7671                      ## END_TAG_OPTIONAL_EL
7672                    dd => ($token->{tag_name} ne 'dd'),                    dd => ($token->{tag_name} ne 'dd'),
7673                    dt => ($token->{tag_name} ne 'dt'),                    dt => ($token->{tag_name} ne 'dt'),
7674                    li => ($token->{tag_name} ne 'li'),                    li => ($token->{tag_name} ne 'li'),
7675                      option => 1,
7676                      optgroup => 1,
7677                    p => 1,                    p => 1,
7678                      rt => 1,
7679                      rp => 1,
7680                   }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {                   }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7681              !!!cp ('t409');              !!!cp ('t409');
7682              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6997  sub _tree_construction_main ($) { Line 7687  sub _tree_construction_main ($) {
7687                    ne $token->{tag_name}) {                    ne $token->{tag_name}) {
7688              !!!cp ('t412');              !!!cp ('t412');
7689              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
7690                              value => $self->{open_elements}->[-1]->[0]                              text => $self->{open_elements}->[-1]->[0]
7691                                  ->manakai_local_name,                                  ->manakai_local_name,
7692                              token => $token);                              token => $token);
7693            } else {            } else {
# Line 7016  sub _tree_construction_main ($) { Line 7706  sub _tree_construction_main ($) {
7706          !!!next-token;          !!!next-token;
7707          next B;          next B;
7708        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
7709            ## NOTE: As normal, but interacts with the form element pointer
7710    
7711          undef $self->{form_element};          undef $self->{form_element};
7712    
7713          ## has an element in scope          ## has an element in scope
# Line 7034  sub _tree_construction_main ($) { Line 7726  sub _tree_construction_main ($) {
7726    
7727          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7728            !!!cp ('t421');            !!!cp ('t421');
7729            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7730                              text => $token->{tag_name}, token => $token);
7731              ## NOTE: Ignore the token.
7732          } else {          } else {
7733            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7734            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
# Line 7047  sub _tree_construction_main ($) { Line 7741  sub _tree_construction_main ($) {
7741                    ne $token->{tag_name}) {                    ne $token->{tag_name}) {
7742              !!!cp ('t417.1');              !!!cp ('t417.1');
7743              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
7744                              value => $self->{open_elements}->[-1]->[0]                              text => $self->{open_elements}->[-1]->[0]
7745                                  ->manakai_local_name,                                  ->manakai_local_name,
7746                              token => $token);                              token => $token);
7747            } else {            } else {
# Line 7061  sub _tree_construction_main ($) { Line 7755  sub _tree_construction_main ($) {
7755          !!!next-token;          !!!next-token;
7756          next B;          next B;
7757        } elsif ({        } elsif ({
7758                    ## NOTE: As normal, except acts as a closer for any ...
7759                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7760                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7761          ## has an element in scope          ## has an element in scope
# Line 7079  sub _tree_construction_main ($) { Line 7774  sub _tree_construction_main ($) {
7774    
7775          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7776            !!!cp ('t425.1');            !!!cp ('t425.1');
7777            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7778                              text => $token->{tag_name}, token => $token);
7779              ## NOTE: Ignore the token.
7780          } else {          } else {
7781            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7782            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
# Line 7091  sub _tree_construction_main ($) { Line 7788  sub _tree_construction_main ($) {
7788            if ($self->{open_elements}->[-1]->[0]->manakai_local_name            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7789                    ne $token->{tag_name}) {                    ne $token->{tag_name}) {
7790              !!!cp ('t425');              !!!cp ('t425');
7791              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
7792                                text => $token->{tag_name}, token => $token);
7793            } else {            } else {
7794              !!!cp ('t426');              !!!cp ('t426');
7795            }            }
# Line 7103  sub _tree_construction_main ($) { Line 7801  sub _tree_construction_main ($) {
7801          !!!next-token;          !!!next-token;
7802          next B;          next B;
7803        } elsif ($token->{tag_name} eq 'p') {        } elsif ($token->{tag_name} eq 'p') {
7804            ## NOTE: As normal, except </p> implies <p> and ...
7805    
7806          ## has an element in scope          ## has an element in scope
7807            my $non_optional;
7808          my $i;          my $i;
7809          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7810            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
# Line 7114  sub _tree_construction_main ($) { Line 7815  sub _tree_construction_main ($) {
7815            } elsif ($node->[1] & SCOPING_EL) {            } elsif ($node->[1] & SCOPING_EL) {
7816              !!!cp ('t411.1');              !!!cp ('t411.1');
7817              last INSCOPE;              last INSCOPE;
7818              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7819                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
7820                !!!cp ('t411.2');
7821                #
7822              } else {
7823                !!!cp ('t411.3');
7824                $non_optional ||= $node;
7825                #
7826            }            }
7827          } # INSCOPE          } # INSCOPE
7828    
7829          if (defined $i) {          if (defined $i) {
7830            if ($self->{open_elements}->[-1]->[0]->manakai_local_name            ## 1. Generate implied end tags
7831                    ne $token->{tag_name}) {            #
7832    
7833              ## 2. If current node != "p", parse error
7834              if ($non_optional) {
7835              !!!cp ('t412.1');              !!!cp ('t412.1');
7836              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
7837                              value => $self->{open_elements}->[-1]->[0]                              text => $non_optional->[0]->manakai_local_name,
                                 ->manakai_local_name,  
7838                              token => $token);                              token => $token);
7839            } else {            } else {
7840              !!!cp ('t414.1');              !!!cp ('t414.1');
7841            }            }
7842    
7843              ## 3. Pop
7844            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
7845          } else {          } else {
7846            !!!cp ('t413.1');            !!!cp ('t413.1');
7847            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7848                              text => $token->{tag_name}, token => $token);
7849    
7850            !!!cp ('t415.1');            !!!cp ('t415.1');
7851            ## As if <p>, then reprocess the current token            ## As if <p>, then reprocess the current token
# Line 7147  sub _tree_construction_main ($) { Line 7860  sub _tree_construction_main ($) {
7860        } elsif ({        } elsif ({
7861                  a => 1,                  a => 1,
7862                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
7863                  nobr => 1, s => 1, small => 1, strile => 1,                  nobr => 1, s => 1, small => 1, strike => 1,
7864                  strong => 1, tt => 1, u => 1,                  strong => 1, tt => 1, u => 1,
7865                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7866          !!!cp ('t427');          !!!cp ('t427');
# Line 7155  sub _tree_construction_main ($) { Line 7868  sub _tree_construction_main ($) {
7868          next B;          next B;
7869        } elsif ($token->{tag_name} eq 'br') {        } elsif ($token->{tag_name} eq 'br') {
7870          !!!cp ('t428');          !!!cp ('t428');
7871          !!!parse-error (type => 'unmatched end tag:br', token => $token);          !!!parse-error (type => 'unmatched end tag',
7872                            text => 'br', token => $token);
7873    
7874          ## As if <br>          ## As if <br>
7875          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
# Line 7167  sub _tree_construction_main ($) { Line 7881  sub _tree_construction_main ($) {
7881          ## Ignore the token.          ## Ignore the token.
7882          !!!next-token;          !!!next-token;
7883          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:'.$token->{tag_name}, token => $token);  
         ## Ignore the token  
         !!!next-token;  
         next B;  
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
7884        } else {        } else {
7885            if ($token->{tag_name} eq 'sarcasm') {
7886              sleep 0.001; # take a deep breath
7887            }
7888    
7889          ## Step 1          ## Step 1
7890          my $node_i = -1;          my $node_i = -1;
7891          my $node = $self->{open_elements}->[$node_i];          my $node = $self->{open_elements}->[$node_i];
7892    
7893          ## Step 2          ## Step 2
7894          S2: {          S2: {
7895            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {            my $node_tag_name = $node->[0]->manakai_local_name;
7896              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
7897              if ($node_tag_name eq $token->{tag_name}) {
7898              ## Step 1              ## Step 1
7899              ## generate implied end tags              ## generate implied end tags
7900              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7901                !!!cp ('t430');                !!!cp ('t430');
7902                ## ISSUE: Can this case be reached?                ## NOTE: |<ruby><rt></ruby>|.
7903                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7904                  ## which seems wrong.
7905                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
7906                  $node_i++;
7907              }              }
7908                    
7909              ## Step 2              ## Step 2
7910              if ($self->{open_elements}->[-1]->[0]->manakai_local_name              my $current_tag_name
7911                      ne $token->{tag_name}) {                  = $self->{open_elements}->[-1]->[0]->manakai_local_name;
7912                $current_tag_name =~ tr/A-Z/a-z/;
7913                if ($current_tag_name ne $token->{tag_name}) {
7914                !!!cp ('t431');                !!!cp ('t431');
7915                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
7916                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
7917                                value => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
7918                                    ->manakai_local_name,                                    ->manakai_local_name,
7919                                token => $token);                                token => $token);
7920              } else {              } else {
# Line 7217  sub _tree_construction_main ($) { Line 7922  sub _tree_construction_main ($) {
7922              }              }
7923                            
7924              ## Step 3              ## Step 3
7925              splice @{$self->{open_elements}}, $node_i;              splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7926    
7927              !!!next-token;              !!!next-token;
7928              last S2;              last S2;
# Line 7228  sub _tree_construction_main ($) { Line 7933  sub _tree_construction_main ($) {
7933                  ($node->[1] & SPECIAL_EL or                  ($node->[1] & SPECIAL_EL or
7934                   $node->[1] & SCOPING_EL)) {                   $node->[1] & SCOPING_EL)) {
7935                !!!cp ('t433');                !!!cp ('t433');
7936                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
7937                                  text => $token->{tag_name}, token => $token);
7938                ## Ignore the token                ## Ignore the token
7939                !!!next-token;                !!!next-token;
7940                last S2;                last S2;
             }  
7941    
7942                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7943                  ## 9.27, "a" is a child of <dd> (conforming).  In
7944                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7945                  ## "a" is a child of both <body> and <dd>.
7946                }
7947                
7948              !!!cp ('t434');              !!!cp ('t434');
7949            }            }
7950                        
# Line 7274  sub _tree_construction_main ($) { Line 7985  sub _tree_construction_main ($) {
7985    ## TODO: script stuffs    ## TODO: script stuffs
7986  } # _tree_construct_main  } # _tree_construct_main
7987    
7988  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
7989    my $class = shift;    my $class = shift;
7990    my $node = shift;    my $node = shift;
7991    my $s = \$_[0];    #my $s = \$_[0];
7992    my $onerror = $_[1];    my $onerror = $_[1];
7993      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
7994    
7995    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
7996    
# Line 7297  sub set_inner_html ($$$) { Line 8009  sub set_inner_html ($$$) {
8009      }      }
8010    
8011      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
8012      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
8013    } elsif ($nt == 1) {    } elsif ($nt == 1) {
8014      ## TODO: If non-html element      ## TODO: If non-html element
8015    
8016      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
8017    
8018    ## TODO: Support for $get_wrapper
8019    
8020      ## Step 1 # MUST      ## Step 1 # MUST
8021      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
8022      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 7314  sub set_inner_html ($$$) { Line 8028  sub set_inner_html ($$$) {
8028      my $i = 0;      my $i = 0;
8029      $p->{line_prev} = $p->{line} = 1;      $p->{line_prev} = $p->{line} = 1;
8030      $p->{column_prev} = $p->{column} = 0;      $p->{column_prev} = $p->{column} = 0;
8031      $p->{set_next_char} = sub {      require Whatpm::Charset::DecodeHandle;
8032        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
8033        $input = $get_wrapper->($input);
8034        $p->{set_nc} = sub {
8035        my $self = shift;        my $self = shift;
8036    
8037        pop @{$self->{prev_char}};        my $char = '';
8038        unshift @{$self->{prev_char}}, $self->{next_char};        if (defined $self->{next_nc}) {
8039            $char = $self->{next_nc};
8040        $self->{next_char} = -1 and return if $i >= length $$s;          delete $self->{next_nc};
8041        $self->{next_char} = ord substr $$s, $i++, 1;          $self->{nc} = ord $char;
8042          } else {
8043            $self->{char_buffer} = '';
8044            $self->{char_buffer_pos} = 0;
8045            
8046            my $count = $input->manakai_read_until
8047                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
8048                 $self->{char_buffer_pos});
8049            if ($count) {
8050              $self->{line_prev} = $self->{line};
8051              $self->{column_prev} = $self->{column};
8052              $self->{column}++;
8053              $self->{nc}
8054                  = ord substr ($self->{char_buffer},
8055                                $self->{char_buffer_pos}++, 1);
8056              return;
8057            }
8058            
8059            if ($input->read ($char, 1)) {
8060              $self->{nc} = ord $char;
8061            } else {
8062              $self->{nc} = -1;
8063              return;
8064            }
8065          }
8066    
8067        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8068        $p->{column}++;        $p->{column}++;
8069    
8070        if ($self->{next_char} == 0x000A) { # LF        if ($self->{nc} == 0x000A) { # LF
8071          $p->{line}++;          $p->{line}++;
8072          $p->{column} = 0;          $p->{column} = 0;
8073          !!!cp ('i1');          !!!cp ('i1');
8074        } elsif ($self->{next_char} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
8075          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
8076          $self->{next_char} = 0x000A; # LF # MUST          my $next = '';
8077            if ($input->read ($next, 1) and $next ne "\x0A") {
8078              $self->{next_nc} = $next;
8079            }
8080            $self->{nc} = 0x000A; # LF # MUST
8081          $p->{line}++;          $p->{line}++;
8082          $p->{column} = 0;          $p->{column} = 0;
8083          !!!cp ('i2');          !!!cp ('i2');
8084        } 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  
8085          !!!cp ('i4');          !!!cp ('i4');
8086          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
8087          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
       } elsif ($self->{next_char} <= 0x0008 or  
                (0x000E <= $self->{next_char} and  
                 $self->{next_char} <= 0x001F) or  
                (0x007F <= $self->{next_char} and  
                 $self->{next_char} <= 0x009F) or  
                (0xD800 <= $self->{next_char} and  
                 $self->{next_char} <= 0xDFFF) or  
                (0xFDD0 <= $self->{next_char} and  
                 $self->{next_char} <= 0xFDDF) or  
                {  
                 0xFFFE => 1, 0xFFFF => 1, 0x1FFFE => 1, 0x1FFFF => 1,  
                 0x2FFFE => 1, 0x2FFFF => 1, 0x3FFFE => 1, 0x3FFFF => 1,  
                 0x4FFFE => 1, 0x4FFFF => 1, 0x5FFFE => 1, 0x5FFFF => 1,  
                 0x6FFFE => 1, 0x6FFFF => 1, 0x7FFFE => 1, 0x7FFFF => 1,  
                 0x8FFFE => 1, 0x8FFFF => 1, 0x9FFFE => 1, 0x9FFFF => 1,  
                 0xAFFFE => 1, 0xAFFFF => 1, 0xBFFFE => 1, 0xBFFFF => 1,  
                 0xCFFFE => 1, 0xCFFFF => 1, 0xDFFFE => 1, 0xDFFFF => 1,  
                 0xEFFFE => 1, 0xEFFFF => 1, 0xFFFFE => 1, 0xFFFFF => 1,  
                 0x10FFFE => 1, 0x10FFFF => 1,  
                }->{$self->{next_char}}) {  
         !!!cp ('i4.1');  
         !!!parse-error (type => 'control char', level => $self->{must_level});  
 ## TODO: error type documentation  
8088        }        }
8089      };      };
8090      $p->{prev_char} = [-1, -1, -1];  
8091      $p->{next_char} = -1;      $p->{read_until} = sub {
8092              #my ($scalar, $specials_range, $offset) = @_;
8093          return 0 if defined $p->{next_nc};
8094    
8095          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
8096          my $offset = $_[2] || 0;
8097          
8098          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
8099            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
8100            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
8101              substr ($_[0], $offset)
8102                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
8103              my $count = $+[0] - $-[0];
8104              if ($count) {
8105                $p->{column} += $count;
8106                $p->{char_buffer_pos} += $count;
8107                $p->{line_prev} = $p->{line};
8108                $p->{column_prev} = $p->{column} - 1;
8109                $p->{nc} = -1;
8110              }
8111              return $count;
8112            } else {
8113              return 0;
8114            }
8115          } else {
8116            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
8117            if ($count) {
8118              $p->{column} += $count;
8119              $p->{column_prev} += $count;
8120              $p->{nc} = -1;
8121            }
8122            return $count;
8123          }
8124        }; # $p->{read_until}
8125    
8126      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
8127        my (%opt) = @_;        my (%opt) = @_;
8128        my $line = $opt{line};        my $line = $opt{line};
# Line 7385  sub set_inner_html ($$$) { Line 8137  sub set_inner_html ($$$) {
8137        $ponerror->(line => $p->{line}, column => $p->{column}, @_);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
8138      };      };
8139            
8140        my $char_onerror = sub {
8141          my (undef, $type, %opt) = @_;
8142          $ponerror->(layer => 'encode',
8143                      line => $p->{line}, column => $p->{column} + 1,
8144                      %opt, type => $type);
8145        }; # $char_onerror
8146        $input->onerror ($char_onerror);
8147    
8148      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
8149      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
8150    

Legend:
Removed from v.1.142  
changed lines
  Added in v.1.201

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24