/[suikacvs]/markup/html/whatpm/Whatpm/HTML.pm.src
Suika

Diff of /markup/html/whatpm/Whatpm/HTML.pm.src

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.162 by wakaba, Thu Sep 11 09:12:27 2008 UTC revision 1.207 by wakaba, Mon Oct 13 08:27:44 2008 UTC
# Line 3  use strict; Line 3  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4  use Error qw(:try);  use Error qw(:try);
5    
6    ## NOTE: This module don't check all HTML5 parse errors; character
7    ## encoding related parse errors are expected to be handled by relevant
8    ## modules.
9    ## Parse errors for control characters that are not allowed in HTML5
10    ## documents, for surrogate code points, and for noncharacter code
11    ## points, as well as U+FFFD substitions for characters whose code points
12    ## is higher than U+10FFFF may be detected by combining the parser with
13    ## the checker implemented by Whatpm::Charset::UnicodeChecker (for its
14    ## usage example, see |t/HTML-tree.t| in the Whatpm package or the
15    ## WebHACC::Language::HTML module in the WebHACC package).
16    
17  ## ISSUE:  ## ISSUE:
18  ## var doc = implementation.createDocument (null, null, null);  ## var doc = implementation.createDocument (null, null, null);
19  ## doc.write ('');  ## doc.write ('');
# Line 17  my $XLINK_NS = q<http://www.w3.org/1999/ Line 28  my $XLINK_NS = q<http://www.w3.org/1999/
28  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
29  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
30    
31  sub A_EL () { 0b1 }  ## Bits 12-15
32  sub ADDRESS_EL () { 0b10 }  sub SPECIAL_EL () { 0b1_000000000000000 }
33  sub BODY_EL () { 0b100 }  sub SCOPING_EL () { 0b1_00000000000000 }
34  sub BUTTON_EL () { 0b1000 }  sub FORMATTING_EL () { 0b1_0000000000000 }
35  sub CAPTION_EL () { 0b10000 }  sub PHRASING_EL () { 0b1_000000000000 }
36  sub DD_EL () { 0b100000 }  
37  sub DIV_EL () { 0b1000000 }  ## Bits 10-11
38  sub DT_EL () { 0b10000000 }  sub FOREIGN_EL () { 0b1_00000000000 }
39  sub FORM_EL () { 0b100000000 }  sub FOREIGN_FLOW_CONTENT_EL () { 0b1_0000000000 }
40  sub FORMATTING_EL () { 0b1000000000 }  
41  sub FRAMESET_EL () { 0b10000000000 }  ## Bits 6-9
42  sub HEADING_EL () { 0b100000000000 }  sub TABLE_SCOPING_EL () { 0b1_000000000 }
43  sub HTML_EL () { 0b1000000000000 }  sub TABLE_ROWS_SCOPING_EL () { 0b1_00000000 }
44  sub LI_EL () { 0b10000000000000 }  sub TABLE_ROW_SCOPING_EL () { 0b1_0000000 }
45  sub NOBR_EL () { 0b100000000000000 }  sub TABLE_ROWS_EL () { 0b1_000000 }
 sub OPTION_EL () { 0b1000000000000000 }  
 sub OPTGROUP_EL () { 0b10000000000000000 }  
 sub P_EL () { 0b100000000000000000 }  
 sub SELECT_EL () { 0b1000000000000000000 }  
 sub TABLE_EL () { 0b10000000000000000000 }  
 sub TABLE_CELL_EL () { 0b100000000000000000000 }  
 sub TABLE_ROW_EL () { 0b1000000000000000000000 }  
 sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }  
 sub MISC_SCOPING_EL () { 0b100000000000000000000000 }  
 sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }  
 sub FOREIGN_EL () { 0b10000000000000000000000000 }  
 sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }  
 sub MML_AXML_EL () { 0b1000000000000000000000000000 }  
 sub RUBY_EL () { 0b10000000000000000000000000000 }  
 sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }  
   
 sub TABLE_ROWS_EL () {  
   TABLE_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL  
 }  
46    
47  ## NOTE: Used in "generate implied end tags" algorithm.  ## Bit 5
48  ## NOTE: There is a code where a modified version of END_TAG_OPTIONAL_EL  sub ADDRESS_DIV_P_EL () { 0b1_00000 }
 ## is used in "generate implied end tags" implementation (search for the  
 ## function mae).  
 sub END_TAG_OPTIONAL_EL () {  
   DD_EL |  
   DT_EL |  
   LI_EL |  
   P_EL |  
   RUBY_COMPONENT_EL  
 }  
49    
50  ## NOTE: Used in </body> and EOF algorithms.  ## NOTE: Used in </body> and EOF algorithms.
51  sub ALL_END_TAG_OPTIONAL_EL () {  ## Bit 4
52    DD_EL |  sub ALL_END_TAG_OPTIONAL_EL () { 0b1_0000 }
   DT_EL |  
   LI_EL |  
   P_EL |  
   
   BODY_EL |  
   HTML_EL |  
   TABLE_CELL_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL  
 }  
53    
54  sub SCOPING_EL () {  ## NOTE: Used in "generate implied end tags" algorithm.
55    BUTTON_EL |  ## NOTE: There is a code where a modified version of
56    CAPTION_EL |  ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
57    HTML_EL |  ## implementation (search for the algorithm name).
58    TABLE_EL |  ## Bit 3
59    TABLE_CELL_EL |  sub END_TAG_OPTIONAL_EL () { 0b1_000 }
60    MISC_SCOPING_EL  
61    ## Bits 0-2
62    
63    sub MISC_SPECIAL_EL () { SPECIAL_EL | 0b000 }
64    sub FORM_EL () { SPECIAL_EL | 0b001 }
65    sub FRAMESET_EL () { SPECIAL_EL | 0b010 }
66    sub HEADING_EL () { SPECIAL_EL | 0b011 }
67    sub SELECT_EL () { SPECIAL_EL | 0b100 }
68    sub SCRIPT_EL () { SPECIAL_EL | 0b101 }
69    
70    sub ADDRESS_DIV_EL () { SPECIAL_EL | ADDRESS_DIV_P_EL | 0b001 }
71    sub BODY_EL () { SPECIAL_EL | ALL_END_TAG_OPTIONAL_EL | 0b001 }
72    
73    sub DTDD_EL () {
74      SPECIAL_EL |
75      END_TAG_OPTIONAL_EL |
76      ALL_END_TAG_OPTIONAL_EL |
77      0b010
78  }  }
79    sub LI_EL () {
80  sub TABLE_SCOPING_EL () {    SPECIAL_EL |
81    HTML_EL |    END_TAG_OPTIONAL_EL |
82    TABLE_EL    ALL_END_TAG_OPTIONAL_EL |
83      0b100
84  }  }
85    sub P_EL () {
86  sub TABLE_ROWS_SCOPING_EL () {    SPECIAL_EL |
87    HTML_EL |    ADDRESS_DIV_P_EL |
88    TABLE_ROW_GROUP_EL    END_TAG_OPTIONAL_EL |
89      ALL_END_TAG_OPTIONAL_EL |
90      0b001
91  }  }
92    
93  sub TABLE_ROW_SCOPING_EL () {  sub TABLE_ROW_EL () {
94    HTML_EL |    SPECIAL_EL |
95    TABLE_ROW_EL    TABLE_ROWS_EL |
96      TABLE_ROW_SCOPING_EL |
97      ALL_END_TAG_OPTIONAL_EL |
98      0b001
99    }
100    sub TABLE_ROW_GROUP_EL () {
101      SPECIAL_EL |
102      TABLE_ROWS_EL |
103      TABLE_ROWS_SCOPING_EL |
104      ALL_END_TAG_OPTIONAL_EL |
105      0b001
106  }  }
107    
108  sub SPECIAL_EL () {  sub MISC_SCOPING_EL () { SCOPING_EL | 0b000 }
109    ADDRESS_EL |  sub BUTTON_EL () { SCOPING_EL | 0b001 }
110    BODY_EL |  sub CAPTION_EL () { SCOPING_EL | 0b010 }
111    DIV_EL |  sub HTML_EL () {
112      SCOPING_EL |
113    DD_EL |    TABLE_SCOPING_EL |
114    DT_EL |    TABLE_ROWS_SCOPING_EL |
115    LI_EL |    TABLE_ROW_SCOPING_EL |
116    P_EL |    ALL_END_TAG_OPTIONAL_EL |
117      0b001
118    FORM_EL |  }
119    FRAMESET_EL |  sub TABLE_EL () {
120    HEADING_EL |    SCOPING_EL |
121    OPTION_EL |    TABLE_ROWS_EL |
122    OPTGROUP_EL |    TABLE_SCOPING_EL |
123    SELECT_EL |    0b001
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL |  
   MISC_SPECIAL_EL  
124  }  }
125    sub TABLE_CELL_EL () {
126      SCOPING_EL |
127      TABLE_ROW_SCOPING_EL |
128      ALL_END_TAG_OPTIONAL_EL |
129      0b001
130    }
131    
132    sub MISC_FORMATTING_EL () { FORMATTING_EL | 0b000 }
133    sub A_EL () { FORMATTING_EL | 0b001 }
134    sub NOBR_EL () { FORMATTING_EL | 0b010 }
135    
136    sub RUBY_EL () { PHRASING_EL | 0b001 }
137    
138    ## ISSUE: ALL_END_TAG_OPTIONAL_EL?
139    sub OPTGROUP_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b001 }
140    sub OPTION_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b010 }
141    sub RUBY_COMPONENT_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b100 }
142    
143    sub MML_AXML_EL () { PHRASING_EL | FOREIGN_EL | 0b001 }
144    
145  my $el_category = {  my $el_category = {
146    a => A_EL | FORMATTING_EL,    a => A_EL,
147    address => ADDRESS_EL,    address => ADDRESS_DIV_EL,
148    applet => MISC_SCOPING_EL,    applet => MISC_SCOPING_EL,
149    area => MISC_SPECIAL_EL,    area => MISC_SPECIAL_EL,
150      article => MISC_SPECIAL_EL,
151      aside => MISC_SPECIAL_EL,
152    b => FORMATTING_EL,    b => FORMATTING_EL,
153    base => MISC_SPECIAL_EL,    base => MISC_SPECIAL_EL,
154    basefont => MISC_SPECIAL_EL,    basefont => MISC_SPECIAL_EL,
# Line 143  my $el_category = { Line 162  my $el_category = {
162    center => MISC_SPECIAL_EL,    center => MISC_SPECIAL_EL,
163    col => MISC_SPECIAL_EL,    col => MISC_SPECIAL_EL,
164    colgroup => MISC_SPECIAL_EL,    colgroup => MISC_SPECIAL_EL,
165    dd => DD_EL,    command => MISC_SPECIAL_EL,
166      datagrid => MISC_SPECIAL_EL,
167      dd => DTDD_EL,
168      details => MISC_SPECIAL_EL,
169      dialog => MISC_SPECIAL_EL,
170    dir => MISC_SPECIAL_EL,    dir => MISC_SPECIAL_EL,
171    div => DIV_EL,    div => ADDRESS_DIV_EL,
172    dl => MISC_SPECIAL_EL,    dl => MISC_SPECIAL_EL,
173    dt => DT_EL,    dt => DTDD_EL,
174    em => FORMATTING_EL,    em => FORMATTING_EL,
175    embed => MISC_SPECIAL_EL,    embed => MISC_SPECIAL_EL,
176      eventsource => MISC_SPECIAL_EL,
177    fieldset => MISC_SPECIAL_EL,    fieldset => MISC_SPECIAL_EL,
178      figure => MISC_SPECIAL_EL,
179    font => FORMATTING_EL,    font => FORMATTING_EL,
180      footer => MISC_SPECIAL_EL,
181    form => FORM_EL,    form => FORM_EL,
182    frame => MISC_SPECIAL_EL,    frame => MISC_SPECIAL_EL,
183    frameset => FRAMESET_EL,    frameset => FRAMESET_EL,
# Line 162  my $el_category = { Line 188  my $el_category = {
188    h5 => HEADING_EL,    h5 => HEADING_EL,
189    h6 => HEADING_EL,    h6 => HEADING_EL,
190    head => MISC_SPECIAL_EL,    head => MISC_SPECIAL_EL,
191      header => MISC_SPECIAL_EL,
192    hr => MISC_SPECIAL_EL,    hr => MISC_SPECIAL_EL,
193    html => HTML_EL,    html => HTML_EL,
194    i => FORMATTING_EL,    i => FORMATTING_EL,
195    iframe => MISC_SPECIAL_EL,    iframe => MISC_SPECIAL_EL,
196    img => MISC_SPECIAL_EL,    img => MISC_SPECIAL_EL,
197      #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
198    input => MISC_SPECIAL_EL,    input => MISC_SPECIAL_EL,
199    isindex => MISC_SPECIAL_EL,    isindex => MISC_SPECIAL_EL,
200    li => LI_EL,    li => LI_EL,
# Line 175  my $el_category = { Line 203  my $el_category = {
203    marquee => MISC_SCOPING_EL,    marquee => MISC_SCOPING_EL,
204    menu => MISC_SPECIAL_EL,    menu => MISC_SPECIAL_EL,
205    meta => MISC_SPECIAL_EL,    meta => MISC_SPECIAL_EL,
206    nobr => NOBR_EL | FORMATTING_EL,    nav => MISC_SPECIAL_EL,
207      nobr => NOBR_EL,
208    noembed => MISC_SPECIAL_EL,    noembed => MISC_SPECIAL_EL,
209    noframes => MISC_SPECIAL_EL,    noframes => MISC_SPECIAL_EL,
210    noscript => MISC_SPECIAL_EL,    noscript => MISC_SPECIAL_EL,
# Line 193  my $el_category = { Line 222  my $el_category = {
222    s => FORMATTING_EL,    s => FORMATTING_EL,
223    script => MISC_SPECIAL_EL,    script => MISC_SPECIAL_EL,
224    select => SELECT_EL,    select => SELECT_EL,
225      section => MISC_SPECIAL_EL,
226    small => FORMATTING_EL,    small => FORMATTING_EL,
227    spacer => MISC_SPECIAL_EL,    spacer => MISC_SPECIAL_EL,
228    strike => FORMATTING_EL,    strike => FORMATTING_EL,
# Line 216  my $el_category = { Line 246  my $el_category = {
246  my $el_category_f = {  my $el_category_f = {
247    $MML_NS => {    $MML_NS => {
248      'annotation-xml' => MML_AXML_EL,      'annotation-xml' => MML_AXML_EL,
249      mi => FOREIGN_FLOW_CONTENT_EL,      mi => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
250      mo => FOREIGN_FLOW_CONTENT_EL,      mo => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
251      mn => FOREIGN_FLOW_CONTENT_EL,      mn => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
252      ms => FOREIGN_FLOW_CONTENT_EL,      ms => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
253      mtext => FOREIGN_FLOW_CONTENT_EL,      mtext => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
254    },    },
255    $SVG_NS => {    $SVG_NS => {
256      foreignObject => FOREIGN_FLOW_CONTENT_EL,      foreignObject => SCOPING_EL | FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
257      desc => FOREIGN_FLOW_CONTENT_EL,      desc => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
258      title => FOREIGN_FLOW_CONTENT_EL,      title => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
259    },    },
260    ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.    ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
261  };  };
# Line 312  my $foreign_attr_xname = { Line 342  my $foreign_attr_xname = {
342    
343  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
344    
345  my $c1_entity_char = {  my $charref_map = {
346      0x0D => 0x000A,
347    0x80 => 0x20AC,    0x80 => 0x20AC,
348    0x81 => 0xFFFD,    0x81 => 0xFFFD,
349    0x82 => 0x201A,    0x82 => 0x201A,
# Line 345  my $c1_entity_char = { Line 376  my $c1_entity_char = {
376    0x9D => 0xFFFD,    0x9D => 0xFFFD,
377    0x9E => 0x017E,    0x9E => 0x017E,
378    0x9F => 0x0178,    0x9F => 0x0178,
379  }; # $c1_entity_char  }; # $charref_map
380    $charref_map->{$_} = 0xFFFD
381        for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
382            0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
383            0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
384            0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
385            0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
386            0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
387            0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
388    
389    ## TODO: Invoke the reset algorithm when a resettable element is
390    ## created (cf. HTML5 revision 2259).
391    
392  sub parse_byte_string ($$$$;$) {  sub parse_byte_string ($$$$;$) {
393    my $self = shift;    my $self = shift;
# Line 390  sub parse_byte_stream ($$$$;$$) { Line 432  sub parse_byte_stream ($$$$;$$) {
432            ## TODO: Is this ok?  Transfer protocol's parameter should be            ## TODO: Is this ok?  Transfer protocol's parameter should be
433            ## interpreted in its semantics?            ## interpreted in its semantics?
434    
       ## ISSUE: Unsupported encoding is not ignored according to the spec.  
435        ($char_stream, $e_status) = $charset->get_decode_handle        ($char_stream, $e_status) = $charset->get_decode_handle
436            ($byte_stream, allow_error_reporting => 1,            ($byte_stream, allow_error_reporting => 1,
437             allow_fallback => 1);             allow_fallback => 1);
# Line 398  sub parse_byte_stream ($$$$;$$) { Line 439  sub parse_byte_stream ($$$$;$$) {
439          $self->{confident} = 1;          $self->{confident} = 1;
440          last SNIFFING;          last SNIFFING;
441        } else {        } else {
442          ## TODO: unsupported error          !!!parse-error (type => 'charset:not supported',
443                            layer => 'encode',
444                            line => 1, column => 1,
445                            value => $charset_name,
446                            level => $self->{level}->{uncertain});
447        }        }
448      }      }
449    
# Line 447  sub parse_byte_stream ($$$$;$$) { Line 492  sub parse_byte_stream ($$$$;$$) {
492      if (defined $charset_name) {      if (defined $charset_name) {
493        $charset = Message::Charset::Info->get_by_html_name ($charset_name);        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
494    
       ## ISSUE: Unsupported encoding is not ignored according to the spec.  
495        require Whatpm::Charset::DecodeHandle;        require Whatpm::Charset::DecodeHandle;
496        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
497            ($byte_stream);            ($byte_stream);
# Line 496  sub parse_byte_stream ($$$$;$$) { Line 540  sub parse_byte_stream ($$$$;$$) {
540                      line => 1, column => 1,                      line => 1, column => 1,
541                      layer => 'encode');                      layer => 'encode');
542    } elsif (not ($e_status &    } elsif (not ($e_status &
543                  Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL())) {                  Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
544      $self->{input_encoding} = $charset->get_iana_name;      $self->{input_encoding} = $charset->get_iana_name;
545      !!!parse-error (type => 'chardecode:no error',      !!!parse-error (type => 'chardecode:no error',
546                      text => $self->{input_encoding},                      text => $self->{input_encoding},
# Line 561  sub parse_byte_stream ($$$$;$$) { Line 605  sub parse_byte_stream ($$$$;$$) {
605    my $char_onerror = sub {    my $char_onerror = sub {
606      my (undef, $type, %opt) = @_;      my (undef, $type, %opt) = @_;
607      !!!parse-error (layer => 'encode',      !!!parse-error (layer => 'encode',
608                      %opt, type => $type,                      line => $self->{line}, column => $self->{column} + 1,
609                      line => $self->{line}, column => $self->{column} + 1);                      %opt, type => $type);
610      if ($opt{octets}) {      if ($opt{octets}) {
611        ${$opt{octets}} = "\x{FFFD}"; # relacement character        ${$opt{octets}} = "\x{FFFD}"; # relacement character
612      }      }
# Line 571  sub parse_byte_stream ($$$$;$$) { Line 615  sub parse_byte_stream ($$$$;$$) {
615    my $wrapped_char_stream = $get_wrapper->($char_stream);    my $wrapped_char_stream = $get_wrapper->($char_stream);
616    $wrapped_char_stream->onerror ($char_onerror);    $wrapped_char_stream->onerror ($char_onerror);
617    
618    my @args = @_; shift @args; # $s    my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
619    my $return;    my $return;
620    try {    try {
621      $return = $self->parse_char_stream ($wrapped_char_stream, @args);        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
# Line 586  sub parse_byte_stream ($$$$;$$) { Line 630  sub parse_byte_stream ($$$$;$$) {
630                        line => 1, column => 1,                        line => 1, column => 1,
631                        layer => 'encode');                        layer => 'encode');
632      } elsif (not ($e_status &      } elsif (not ($e_status &
633                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL())) {                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
634        $self->{input_encoding} = $charset->get_iana_name;        $self->{input_encoding} = $charset->get_iana_name;
635        !!!parse-error (type => 'chardecode:no error',        !!!parse-error (type => 'chardecode:no error',
636                        text => $self->{input_encoding},                        text => $self->{input_encoding},
# Line 618  sub parse_byte_stream ($$$$;$$) { Line 662  sub parse_byte_stream ($$$$;$$) {
662  sub parse_char_string ($$$;$$) {  sub parse_char_string ($$$;$$) {
663    #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;    #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
664    my $self = shift;    my $self = shift;
   require utf8;  
665    my $s = ref $_[0] ? $_[0] : \($_[0]);    my $s = ref $_[0] ? $_[0] : \($_[0]);
666    open my $input, '<' . (utf8::is_utf8 ($$s) ? ':utf8' : ''), $s;    require Whatpm::Charset::DecodeHandle;
667    if ($_[3]) {    my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
     $input = $_[3]->($input);  
   }  
668    return $self->parse_char_stream ($input, @_[1..$#_]);    return $self->parse_char_stream ($input, @_[1..$#_]);
669  } # parse_char_string  } # parse_char_string
670  *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.  *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
671    
672  sub parse_char_stream ($$$;$) {  sub parse_char_stream ($$$;$$) {
673    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
674    my $input = $_[0];    my $input = $_[0];
675    $self->{document} = $_[1];    $self->{document} = $_[1];
# Line 639  sub parse_char_stream ($$$;$) { Line 680  sub parse_char_stream ($$$;$) {
680    $self->{confident} = 1 unless exists $self->{confident};    $self->{confident} = 1 unless exists $self->{confident};
681    $self->{document}->input_encoding ($self->{input_encoding})    $self->{document}->input_encoding ($self->{input_encoding})
682        if defined $self->{input_encoding};        if defined $self->{input_encoding};
683    ## TODO: |{input_encoding}| is needless?
684    
   my $i = 0;  
685    $self->{line_prev} = $self->{line} = 1;    $self->{line_prev} = $self->{line} = 1;
686    $self->{column_prev} = $self->{column} = 0;    $self->{column_prev} = -1;
687    $self->{set_next_char} = sub {    $self->{column} = 0;
688      $self->{set_nc} = sub {
689      my $self = shift;      my $self = shift;
690    
691      pop @{$self->{prev_char}};      my $char = '';
692      unshift @{$self->{prev_char}}, $self->{next_char};      if (defined $self->{next_nc}) {
693          $char = $self->{next_nc};
694      my $char;        delete $self->{next_nc};
695      if (defined $self->{next_next_char}) {        $self->{nc} = ord $char;
       $char = $self->{next_next_char};  
       delete $self->{next_next_char};  
696      } else {      } else {
697        $char = $input->getc;        $self->{char_buffer} = '';
698          $self->{char_buffer_pos} = 0;
699    
700          my $count = $input->manakai_read_until
701             ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
702          if ($count) {
703            $self->{line_prev} = $self->{line};
704            $self->{column_prev} = $self->{column};
705            $self->{column}++;
706            $self->{nc}
707                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
708            return;
709          }
710    
711          if ($input->read ($char, 1)) {
712            $self->{nc} = ord $char;
713          } else {
714            $self->{nc} = -1;
715            return;
716          }
717      }      }
     $self->{next_char} = -1 and return unless defined $char;  
     $self->{next_char} = ord $char;  
718    
719      ($self->{line_prev}, $self->{column_prev})      ($self->{line_prev}, $self->{column_prev})
720          = ($self->{line}, $self->{column});          = ($self->{line}, $self->{column});
721      $self->{column}++;      $self->{column}++;
722            
723      if ($self->{next_char} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
724        !!!cp ('j1');        !!!cp ('j1');
725        $self->{line}++;        $self->{line}++;
726        $self->{column} = 0;        $self->{column} = 0;
727      } elsif ($self->{next_char} == 0x000D) { # CR      } elsif ($self->{nc} == 0x000D) { # CR
728        !!!cp ('j2');        !!!cp ('j2');
729        my $next = $input->getc;  ## TODO: support for abort/streaming
730        if (defined $next and $next ne "\x0A") {        my $next = '';
731          $self->{next_next_char} = $next;        if ($input->read ($next, 1) and $next ne "\x0A") {
732            $self->{next_nc} = $next;
733        }        }
734        $self->{next_char} = 0x000A; # LF # MUST        $self->{nc} = 0x000A; # LF # MUST
735        $self->{line}++;        $self->{line}++;
736        $self->{column} = 0;        $self->{column} = 0;
737      } 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  
738        !!!cp ('j4');        !!!cp ('j4');
739        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
740        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
741      } elsif ($self->{next_char} <= 0x0008 or      }
742               (0x000E <= $self->{next_char} and $self->{next_char} <= 0x001F) or    };
743               (0x007F <= $self->{next_char} and $self->{next_char} <= 0x009F) or  
744               (0xD800 <= $self->{next_char} and $self->{next_char} <= 0xDFFF) or    $self->{read_until} = sub {
745               (0xFDD0 <= $self->{next_char} and $self->{next_char} <= 0xFDDF) or      #my ($scalar, $specials_range, $offset) = @_;
746               {      return 0 if defined $self->{next_nc};
747                0xFFFE => 1, 0xFFFF => 1, 0x1FFFE => 1, 0x1FFFF => 1,  
748                0x2FFFE => 1, 0x2FFFF => 1, 0x3FFFE => 1, 0x3FFFF => 1,      my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
749                0x4FFFE => 1, 0x4FFFF => 1, 0x5FFFE => 1, 0x5FFFF => 1,      my $offset = $_[2] || 0;
750                0x6FFFE => 1, 0x6FFFF => 1, 0x7FFFE => 1, 0x7FFFF => 1,  
751                0x8FFFE => 1, 0x8FFFF => 1, 0x9FFFE => 1, 0x9FFFF => 1,      if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
752                0xAFFFE => 1, 0xAFFFF => 1, 0xBFFFE => 1, 0xBFFFF => 1,        pos ($self->{char_buffer}) = $self->{char_buffer_pos};
753                0xCFFFE => 1, 0xCFFFF => 1, 0xDFFFE => 1, 0xDFFFF => 1,        if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
754                0xEFFFE => 1, 0xEFFFF => 1, 0xFFFFE => 1, 0xFFFFF => 1,          substr ($_[0], $offset)
755                0x10FFFE => 1, 0x10FFFF => 1,              = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
756               }->{$self->{next_char}}) {          my $count = $+[0] - $-[0];
757        !!!cp ('j5');          if ($count) {
758        if ($self->{next_char} < 0x10000) {            $self->{column} += $count;
759          !!!parse-error (type => 'control char',            $self->{char_buffer_pos} += $count;
760                          text => (sprintf 'U+%04X', $self->{next_char}));            $self->{line_prev} = $self->{line};
761              $self->{column_prev} = $self->{column} - 1;
762              $self->{nc} = -1;
763            }
764            return $count;
765        } else {        } else {
766          !!!parse-error (type => 'control char',          return 0;
767                          text => (sprintf 'U-%08X', $self->{next_char}));        }
768        } else {
769          my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
770          if ($count) {
771            $self->{column} += $count;
772            $self->{line_prev} = $self->{line};
773            $self->{column_prev} = $self->{column} - 1;
774            $self->{nc} = -1;
775        }        }
776          return $count;
777      }      }
778    };    }; # $self->{read_until}
   $self->{prev_char} = [-1, -1, -1];  
   $self->{next_char} = -1;  
779    
780    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
781      my (%opt) = @_;      my (%opt) = @_;
# Line 722  sub parse_char_stream ($$$;$) { Line 787  sub parse_char_stream ($$$;$) {
787      $onerror->(line => $self->{line}, column => $self->{column}, @_);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
788    };    };
789    
790      my $char_onerror = sub {
791        my (undef, $type, %opt) = @_;
792        !!!parse-error (layer => 'encode',
793                        line => $self->{line}, column => $self->{column} + 1,
794                        %opt, type => $type);
795      }; # $char_onerror
796    
797      if ($_[3]) {
798        $input = $_[3]->($input);
799        $input->onerror ($char_onerror);
800      } else {
801        $input->onerror ($char_onerror) unless defined $input->onerror;
802      }
803    
804    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
805    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
806    $self->_construct_tree;    $self->_construct_tree;
# Line 741  sub new ($) { Line 820  sub new ($) {
820                info => 'i',                info => 'i',
821                uncertain => 'u'},                uncertain => 'u'},
822    }, $class;    }, $class;
823    $self->{set_next_char} = sub {    $self->{set_nc} = sub {
824      $self->{next_char} = -1;      $self->{nc} = -1;
825    };    };
826    $self->{parse_error} = sub {    $self->{parse_error} = sub {
827      #      #
# Line 769  sub RCDATA_CONTENT_MODEL () { CM_ENTITY Line 848  sub RCDATA_CONTENT_MODEL () { CM_ENTITY
848  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
849    
850  sub DATA_STATE () { 0 }  sub DATA_STATE () { 0 }
851  sub ENTITY_DATA_STATE () { 1 }  #sub ENTITY_DATA_STATE () { 1 }
852  sub TAG_OPEN_STATE () { 2 }  sub TAG_OPEN_STATE () { 2 }
853  sub CLOSE_TAG_OPEN_STATE () { 3 }  sub CLOSE_TAG_OPEN_STATE () { 3 }
854  sub TAG_NAME_STATE () { 4 }  sub TAG_NAME_STATE () { 4 }
# Line 780  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 Line 859  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8
859  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
860  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
861  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
862  sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }  #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
863  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
864  sub COMMENT_START_STATE () { 14 }  sub COMMENT_START_STATE () { 14 }
865  sub COMMENT_START_DASH_STATE () { 15 }  sub COMMENT_START_DASH_STATE () { 15 }
# Line 803  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STAT Line 882  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STAT
882  sub BOGUS_DOCTYPE_STATE () { 32 }  sub BOGUS_DOCTYPE_STATE () { 32 }
883  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
884  sub SELF_CLOSING_START_TAG_STATE () { 34 }  sub SELF_CLOSING_START_TAG_STATE () { 34 }
885  sub CDATA_BLOCK_STATE () { 35 }  sub CDATA_SECTION_STATE () { 35 }
886    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
887    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
888    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
889    sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
890    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
891    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
892    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
893    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
894    ## NOTE: "Entity data state", "entity in attribute value state", and
895    ## "consume a character reference" algorithm are jointly implemented
896    ## using the following six states:
897    sub ENTITY_STATE () { 44 }
898    sub ENTITY_HASH_STATE () { 45 }
899    sub NCR_NUM_STATE () { 46 }
900    sub HEXREF_X_STATE () { 47 }
901    sub HEXREF_HEX_STATE () { 48 }
902    sub ENTITY_NAME_STATE () { 49 }
903    sub PCDATA_STATE () { 50 } # "data state" in the spec
904    
905  sub DOCTYPE_TOKEN () { 1 }  sub DOCTYPE_TOKEN () { 1 }
906  sub COMMENT_TOKEN () { 2 }  sub COMMENT_TOKEN () { 2 }
# Line 825  sub IN_FOREIGN_CONTENT_IM () { 0b1000000 Line 922  sub IN_FOREIGN_CONTENT_IM () { 0b1000000
922      ## NOTE: "in foreign content" insertion mode is special; it is combined      ## NOTE: "in foreign content" insertion mode is special; it is combined
923      ## with the secondary insertion mode.  In this parser, they are stored      ## with the secondary insertion mode.  In this parser, they are stored
924      ## together in the bit-or'ed form.      ## together in the bit-or'ed form.
925    sub IN_CDATA_RCDATA_IM () { 0b1000000000000 }
926        ## NOTE: "in CDATA/RCDATA" insertion mode is also special; it is
927        ## combined with the original insertion mode.  In thie parser,
928        ## they are stored together in the bit-or'ed form.
929    
930  ## NOTE: "initial" and "before html" insertion modes have no constants.  ## NOTE: "initial" and "before html" insertion modes have no constants.
931    
# Line 856  sub IN_COLUMN_GROUP_IM () { 0b10 } Line 957  sub IN_COLUMN_GROUP_IM () { 0b10 }
957  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
958    my $self = shift;    my $self = shift;
959    $self->{state} = DATA_STATE; # MUST    $self->{state} = DATA_STATE; # MUST
960      #$self->{s_kwd}; # state keyword - initialized when used
961      #$self->{entity__value}; # initialized when used
962      #$self->{entity__match}; # initialized when used
963    $self->{content_model} = PCDATA_CONTENT_MODEL; # be    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
964    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    undef $self->{ct}; # current token
965    undef $self->{current_attribute};    undef $self->{ca}; # current attribute
966    undef $self->{last_emitted_start_tag_name};    undef $self->{last_stag_name}; # last emitted start tag name
967    undef $self->{last_attribute_value_state};    #$self->{prev_state}; # initialized when used
968    delete $self->{self_closing};    delete $self->{self_closing};
969    $self->{char} = [];    $self->{char_buffer} = '';
970    # $self->{next_char}    $self->{char_buffer_pos} = 0;
971      $self->{nc} = -1; # next input character
972      #$self->{next_nc}
973    !!!next-input-character;    !!!next-input-character;
974    $self->{token} = [];    $self->{token} = [];
975    # $self->{escape}    # $self->{escape}
# Line 874  sub _initialize_tokenizer ($) { Line 980  sub _initialize_tokenizer ($) {
980  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
981  ##   ->{name} (DOCTYPE_TOKEN)  ##   ->{name} (DOCTYPE_TOKEN)
982  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
983  ##   ->{public_identifier} (DOCTYPE_TOKEN)  ##   ->{pubid} (DOCTYPE_TOKEN)
984  ##   ->{system_identifier} (DOCTYPE_TOKEN)  ##   ->{sysid} (DOCTYPE_TOKEN)
985  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
986  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
987  ##        ->{name}  ##        ->{name}
# Line 894  sub _initialize_tokenizer ($) { Line 1000  sub _initialize_tokenizer ($) {
1000  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
1001  ## and removed from the list.  ## and removed from the list.
1002    
1003  ## NOTE: HTML5 "Writing HTML documents" section, applied to  ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
1004  ## documents and not to user agents and conformance checkers,  ## (This requirement was dropped from HTML5 spec, unfortunately.)
1005  ## contains some requirements that are not detected by the  
1006  ## parsing algorithm:  my $is_space = {
1007  ## - Some requirements on character encoding declarations. ## TODO    0x0009 => 1, # CHARACTER TABULATION (HT)
1008  ## - "Elements MUST NOT contain content that their content model disallows."    0x000A => 1, # LINE FEED (LF)
1009  ##   ... Some are parse error, some are not (will be reported by c.c.).    #0x000B => 0, # LINE TABULATION (VT)
1010  ## - Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) ## TODO    0x000C => 1, # FORM FEED (FF)
1011  ## - Text (in elements, attributes, and comments) SHOULD NOT contain    #0x000D => 1, # CARRIAGE RETURN (CR)
1012  ##   control characters other than space characters. ## TODO: (what is control character? C0, C1 and DEL?  Unicode control character?)    0x0020 => 1, # SPACE (SP)
1013    };
 ## TODO: HTML5 poses authors two SHOULD-level requirements that cannot  
 ## be detected by the HTML5 parsing algorithm:  
 ## - Text,  
1014    
1015  sub _get_next_token ($) {  sub _get_next_token ($) {
1016    my $self = shift;    my $self = shift;
1017    
1018    if ($self->{self_closing}) {    if ($self->{self_closing}) {
1019      !!!parse-error (type => 'nestc', token => $self->{current_token});      !!!parse-error (type => 'nestc', token => $self->{ct});
1020      ## NOTE: The |self_closing| flag is only set by start tag token.      ## NOTE: The |self_closing| flag is only set by start tag token.
1021      ## 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
1022      ## |current_token|.      ## |ct|.
1023      delete $self->{self_closing};      delete $self->{self_closing};
1024    }    }
1025    
# Line 926  sub _get_next_token ($) { Line 1029  sub _get_next_token ($) {
1029    }    }
1030    
1031    A: {    A: {
1032      if ($self->{state} == DATA_STATE) {      if ($self->{state} == PCDATA_STATE) {
1033        if ($self->{next_char} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1034    
1035          if ($self->{nc} == 0x0026) { # &
1036            !!!cp (0.1);
1037            ## NOTE: In the spec, the tokenizer is switched to the
1038            ## "entity data state".  In this implementation, the tokenizer
1039            ## is switched to the |ENTITY_STATE|, which is an implementation
1040            ## of the "consume a character reference" algorithm.
1041            $self->{entity_add} = -1;
1042            $self->{prev_state} = DATA_STATE;
1043            $self->{state} = ENTITY_STATE;
1044            !!!next-input-character;
1045            redo A;
1046          } elsif ($self->{nc} == 0x003C) { # <
1047            !!!cp (0.2);
1048            $self->{state} = TAG_OPEN_STATE;
1049            !!!next-input-character;
1050            redo A;
1051          } elsif ($self->{nc} == -1) {
1052            !!!cp (0.3);
1053            !!!emit ({type => END_OF_FILE_TOKEN,
1054                      line => $self->{line}, column => $self->{column}});
1055            last A; ## TODO: ok?
1056          } else {
1057            !!!cp (0.4);
1058            #
1059          }
1060    
1061          # Anything else
1062          my $token = {type => CHARACTER_TOKEN,
1063                       data => chr $self->{nc},
1064                       line => $self->{line}, column => $self->{column},
1065                      };
1066          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1067    
1068          ## Stay in the state.
1069          !!!next-input-character;
1070          !!!emit ($token);
1071          redo A;
1072        } elsif ($self->{state} == DATA_STATE) {
1073          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1074          if ($self->{nc} == 0x0026) { # &
1075            $self->{s_kwd} = '';
1076          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1077              not $self->{escape}) {              not $self->{escape}) {
1078            !!!cp (1);            !!!cp (1);
1079            $self->{state} = ENTITY_DATA_STATE;            ## NOTE: In the spec, the tokenizer is switched to the
1080              ## "entity data state".  In this implementation, the tokenizer
1081              ## is switched to the |ENTITY_STATE|, which is an implementation
1082              ## of the "consume a character reference" algorithm.
1083              $self->{entity_add} = -1;
1084              $self->{prev_state} = DATA_STATE;
1085              $self->{state} = ENTITY_STATE;
1086            !!!next-input-character;            !!!next-input-character;
1087            redo A;            redo A;
1088          } else {          } else {
1089            !!!cp (2);            !!!cp (2);
1090            #            #
1091          }          }
1092        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
1093          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1094            unless ($self->{escape}) {            $self->{s_kwd} .= '-';
1095              if ($self->{prev_char}->[0] == 0x002D and # -            
1096                  $self->{prev_char}->[1] == 0x0021 and # !            if ($self->{s_kwd} eq '<!--') {
1097                  $self->{prev_char}->[2] == 0x003C) { # <              !!!cp (3);
1098                !!!cp (3);              $self->{escape} = 1; # unless $self->{escape};
1099                $self->{escape} = 1;              $self->{s_kwd} = '--';
1100              } else {              #
1101                !!!cp (4);            } elsif ($self->{s_kwd} eq '---') {
1102              }              !!!cp (4);
1103                $self->{s_kwd} = '--';
1104                #
1105            } else {            } else {
1106              !!!cp (5);              !!!cp (5);
1107                #
1108            }            }
1109          }          }
1110                    
1111          #          #
1112        } elsif ($self->{next_char} == 0x003C) { # <        } elsif ($self->{nc} == 0x0021) { # !
1113            if (length $self->{s_kwd}) {
1114              !!!cp (5.1);
1115              $self->{s_kwd} .= '!';
1116              #
1117            } else {
1118              !!!cp (5.2);
1119              #$self->{s_kwd} = '';
1120              #
1121            }
1122            #
1123          } elsif ($self->{nc} == 0x003C) { # <
1124          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1125              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1126               not $self->{escape})) {               not $self->{escape})) {
# Line 965  sub _get_next_token ($) { Line 1130  sub _get_next_token ($) {
1130            redo A;            redo A;
1131          } else {          } else {
1132            !!!cp (7);            !!!cp (7);
1133              $self->{s_kwd} = '';
1134            #            #
1135          }          }
1136        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1137          if ($self->{escape} and          if ($self->{escape} and
1138              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1139            if ($self->{prev_char}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '--') {
               $self->{prev_char}->[1] == 0x002D) { # -  
1140              !!!cp (8);              !!!cp (8);
1141              delete $self->{escape};              delete $self->{escape};
1142            } else {            } else {
# Line 981  sub _get_next_token ($) { Line 1146  sub _get_next_token ($) {
1146            !!!cp (10);            !!!cp (10);
1147          }          }
1148                    
1149            $self->{s_kwd} = '';
1150          #          #
1151        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1152          !!!cp (11);          !!!cp (11);
1153            $self->{s_kwd} = '';
1154          !!!emit ({type => END_OF_FILE_TOKEN,          !!!emit ({type => END_OF_FILE_TOKEN,
1155                    line => $self->{line}, column => $self->{column}});                    line => $self->{line}, column => $self->{column}});
1156          last A; ## TODO: ok?          last A; ## TODO: ok?
1157        } else {        } else {
1158          !!!cp (12);          !!!cp (12);
1159            $self->{s_kwd} = '';
1160            #
1161        }        }
1162    
1163        # Anything else        # Anything else
1164        my $token = {type => CHARACTER_TOKEN,        my $token = {type => CHARACTER_TOKEN,
1165                     data => chr $self->{next_char},                     data => chr $self->{nc},
1166                     line => $self->{line}, column => $self->{column},                     line => $self->{line}, column => $self->{column},
1167                    };                    };
1168        ## Stay in the data state        if ($self->{read_until}->($token->{data}, q[-!<>&],
1169        !!!next-input-character;                                  length $token->{data})) {
1170            $self->{s_kwd} = '';
1171        !!!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  
1172    
1173        unless (defined $token) {        ## Stay in the data state.
1174          if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1175          !!!cp (13);          !!!cp (13);
1176          !!!emit ({type => CHARACTER_TOKEN, data => '&',          $self->{state} = PCDATA_STATE;
                   line => $l, column => $c,  
                  });  
1177        } else {        } else {
1178          !!!cp (14);          !!!cp (14);
1179          !!!emit ($token);          ## Stay in the state.
1180        }        }
1181          !!!next-input-character;
1182          !!!emit ($token);
1183        redo A;        redo A;
1184      } elsif ($self->{state} == TAG_OPEN_STATE) {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1185        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1186          if ($self->{next_char} == 0x002F) { # /          if ($self->{nc} == 0x002F) { # /
1187            !!!cp (15);            !!!cp (15);
1188            !!!next-input-character;            !!!next-input-character;
1189            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1190            redo A;            redo A;
1191            } elsif ($self->{nc} == 0x0021) { # !
1192              !!!cp (15.1);
1193              $self->{s_kwd} = '<' unless $self->{escape};
1194              #
1195          } else {          } else {
1196            !!!cp (16);            !!!cp (16);
1197            ## reconsume            #
           $self->{state} = DATA_STATE;  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev},  
                    });  
   
           redo A;  
1198          }          }
1199    
1200            ## reconsume
1201            $self->{state} = DATA_STATE;
1202            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1203                      line => $self->{line_prev},
1204                      column => $self->{column_prev},
1205                     });
1206            redo A;
1207        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1208          if ($self->{next_char} == 0x0021) { # !          if ($self->{nc} == 0x0021) { # !
1209            !!!cp (17);            !!!cp (17);
1210            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1211            !!!next-input-character;            !!!next-input-character;
1212            redo A;            redo A;
1213          } elsif ($self->{next_char} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1214            !!!cp (18);            !!!cp (18);
1215            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1216            !!!next-input-character;            !!!next-input-character;
1217            redo A;            redo A;
1218          } elsif (0x0041 <= $self->{next_char} and          } elsif (0x0041 <= $self->{nc} and
1219                   $self->{next_char} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1220            !!!cp (19);            !!!cp (19);
1221            $self->{current_token}            $self->{ct}
1222              = {type => START_TAG_TOKEN,              = {type => START_TAG_TOKEN,
1223                 tag_name => chr ($self->{next_char} + 0x0020),                 tag_name => chr ($self->{nc} + 0x0020),
1224                 line => $self->{line_prev},                 line => $self->{line_prev},
1225                 column => $self->{column_prev}};                 column => $self->{column_prev}};
1226            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1227            !!!next-input-character;            !!!next-input-character;
1228            redo A;            redo A;
1229          } elsif (0x0061 <= $self->{next_char} and          } elsif (0x0061 <= $self->{nc} and
1230                   $self->{next_char} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1231            !!!cp (20);            !!!cp (20);
1232            $self->{current_token} = {type => START_TAG_TOKEN,            $self->{ct} = {type => START_TAG_TOKEN,
1233                                      tag_name => chr ($self->{next_char}),                                      tag_name => chr ($self->{nc}),
1234                                      line => $self->{line_prev},                                      line => $self->{line_prev},
1235                                      column => $self->{column_prev}};                                      column => $self->{column_prev}};
1236            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1237            !!!next-input-character;            !!!next-input-character;
1238            redo A;            redo A;
1239          } elsif ($self->{next_char} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1240            !!!cp (21);            !!!cp (21);
1241            !!!parse-error (type => 'empty start tag',            !!!parse-error (type => 'empty start tag',
1242                            line => $self->{line_prev},                            line => $self->{line_prev},
# Line 1087  sub _get_next_token ($) { Line 1250  sub _get_next_token ($) {
1250                     });                     });
1251    
1252            redo A;            redo A;
1253          } elsif ($self->{next_char} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1254            !!!cp (22);            !!!cp (22);
1255            !!!parse-error (type => 'pio',            !!!parse-error (type => 'pio',
1256                            line => $self->{line_prev},                            line => $self->{line_prev},
1257                            column => $self->{column_prev});                            column => $self->{column_prev});
1258            $self->{state} = BOGUS_COMMENT_STATE;            $self->{state} = BOGUS_COMMENT_STATE;
1259            $self->{current_token} = {type => COMMENT_TOKEN, data => '',            $self->{ct} = {type => COMMENT_TOKEN, data => '',
1260                                      line => $self->{line_prev},                                      line => $self->{line_prev},
1261                                      column => $self->{column_prev},                                      column => $self->{column_prev},
1262                                     };                                     };
1263            ## $self->{next_char} is intentionally left as is            ## $self->{nc} is intentionally left as is
1264            redo A;            redo A;
1265          } else {          } else {
1266            !!!cp (23);            !!!cp (23);
# Line 1118  sub _get_next_token ($) { Line 1281  sub _get_next_token ($) {
1281          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
1282        }        }
1283      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1284          ## NOTE: The "close tag open state" in the spec is implemented as
1285          ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
1286    
1287        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1288        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1289          if (defined $self->{last_emitted_start_tag_name}) {          if (defined $self->{last_stag_name}) {
1290              $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1291            ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>            $self->{s_kwd} = '';
1292            my @next_char;            ## Reconsume.
1293            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...  
           }  
1294          } else {          } else {
1295            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1296              ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1297            !!!cp (28);            !!!cp (28);
           # next-input-character is already done  
1298            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1299              ## Reconsume.
1300            !!!emit ({type => CHARACTER_TOKEN, data => '</',            !!!emit ({type => CHARACTER_TOKEN, data => '</',
1301                      line => $l, column => $c,                      line => $l, column => $c,
1302                     });                     });
1303            redo A;            redo A;
1304          }          }
1305        }        }
1306          
1307        if (0x0041 <= $self->{next_char} and        if (0x0041 <= $self->{nc} and
1308            $self->{next_char} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1309          !!!cp (29);          !!!cp (29);
1310          $self->{current_token}          $self->{ct}
1311              = {type => END_TAG_TOKEN,              = {type => END_TAG_TOKEN,
1312                 tag_name => chr ($self->{next_char} + 0x0020),                 tag_name => chr ($self->{nc} + 0x0020),
1313                 line => $l, column => $c};                 line => $l, column => $c};
1314          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1315          !!!next-input-character;          !!!next-input-character;
1316          redo A;          redo A;
1317        } elsif (0x0061 <= $self->{next_char} and        } elsif (0x0061 <= $self->{nc} and
1318                 $self->{next_char} <= 0x007A) { # a..z                 $self->{nc} <= 0x007A) { # a..z
1319          !!!cp (30);          !!!cp (30);
1320          $self->{current_token} = {type => END_TAG_TOKEN,          $self->{ct} = {type => END_TAG_TOKEN,
1321                                    tag_name => chr ($self->{next_char}),                                    tag_name => chr ($self->{nc}),
1322                                    line => $l, column => $c};                                    line => $l, column => $c};
1323          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1324          !!!next-input-character;          !!!next-input-character;
1325          redo A;          redo A;
1326        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1327          !!!cp (31);          !!!cp (31);
1328          !!!parse-error (type => 'empty end tag',          !!!parse-error (type => 'empty end tag',
1329                          line => $self->{line_prev}, ## "<" in "</>"                          line => $self->{line_prev}, ## "<" in "</>"
# Line 1208  sub _get_next_token ($) { Line 1331  sub _get_next_token ($) {
1331          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1332          !!!next-input-character;          !!!next-input-character;
1333          redo A;          redo A;
1334        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1335          !!!cp (32);          !!!cp (32);
1336          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1337          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
# Line 1223  sub _get_next_token ($) { Line 1346  sub _get_next_token ($) {
1346          !!!cp (33);          !!!cp (33);
1347          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1348          $self->{state} = BOGUS_COMMENT_STATE;          $self->{state} = BOGUS_COMMENT_STATE;
1349          $self->{current_token} = {type => COMMENT_TOKEN, data => '',          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1350                                    line => $self->{line_prev}, # "<" of "</"                                    line => $self->{line_prev}, # "<" of "</"
1351                                    column => $self->{column_prev} - 1,                                    column => $self->{column_prev} - 1,
1352                                   };                                   };
1353          ## $self->{next_char} is intentionally left as is          ## NOTE: $self->{nc} is intentionally left as is.
1354          redo A;          ## Although the "anything else" case of the spec not explicitly
1355            ## states that the next input character is to be reconsumed,
1356            ## it will be included to the |data| of the comment token
1357            ## generated from the bogus end tag, as defined in the
1358            ## "bogus comment state" entry.
1359            redo A;
1360          }
1361        } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1362          my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1363          if (length $ch) {
1364            my $CH = $ch;
1365            $ch =~ tr/a-z/A-Z/;
1366            my $nch = chr $self->{nc};
1367            if ($nch eq $ch or $nch eq $CH) {
1368              !!!cp (24);
1369              ## Stay in the state.
1370              $self->{s_kwd} .= $nch;
1371              !!!next-input-character;
1372              redo A;
1373            } else {
1374              !!!cp (25);
1375              $self->{state} = DATA_STATE;
1376              ## Reconsume.
1377              !!!emit ({type => CHARACTER_TOKEN,
1378                        data => '</' . $self->{s_kwd},
1379                        line => $self->{line_prev},
1380                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1381                       });
1382              redo A;
1383            }
1384          } else { # after "<{tag-name}"
1385            unless ($is_space->{$self->{nc}} or
1386                    {
1387                     0x003E => 1, # >
1388                     0x002F => 1, # /
1389                     -1 => 1, # EOF
1390                    }->{$self->{nc}}) {
1391              !!!cp (26);
1392              ## Reconsume.
1393              $self->{state} = DATA_STATE;
1394              !!!emit ({type => CHARACTER_TOKEN,
1395                        data => '</' . $self->{s_kwd},
1396                        line => $self->{line_prev},
1397                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1398                       });
1399              redo A;
1400            } else {
1401              !!!cp (27);
1402              $self->{ct}
1403                  = {type => END_TAG_TOKEN,
1404                     tag_name => $self->{last_stag_name},
1405                     line => $self->{line_prev},
1406                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1407              $self->{state} = TAG_NAME_STATE;
1408              ## Reconsume.
1409              redo A;
1410            }
1411        }        }
1412      } elsif ($self->{state} == TAG_NAME_STATE) {      } elsif ($self->{state} == TAG_NAME_STATE) {
1413        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  
1414          !!!cp (34);          !!!cp (34);
1415          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1416          !!!next-input-character;          !!!next-input-character;
1417          redo A;          redo A;
1418        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1419          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1420            !!!cp (35);            !!!cp (35);
1421            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1422          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1423            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1424            #if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1425            #  ## NOTE: This should never be reached.            #  ## NOTE: This should never be reached.
1426            #  !!! cp (36);            #  !!! cp (36);
1427            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 1254  sub _get_next_token ($) { Line 1429  sub _get_next_token ($) {
1429              !!!cp (37);              !!!cp (37);
1430            #}            #}
1431          } else {          } else {
1432            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1433          }          }
1434          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1435          !!!next-input-character;          !!!next-input-character;
1436    
1437          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1438    
1439          redo A;          redo A;
1440        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1441                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1442          !!!cp (38);          !!!cp (38);
1443          $self->{current_token}->{tag_name} .= chr ($self->{next_char} + 0x0020);          $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1444            # start tag or end tag            # start tag or end tag
1445          ## Stay in this state          ## Stay in this state
1446          !!!next-input-character;          !!!next-input-character;
1447          redo A;          redo A;
1448        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1449          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1450          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1451            !!!cp (39);            !!!cp (39);
1452            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1453          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1454            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1455            #if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1456            #  ## NOTE: This state should never be reached.            #  ## NOTE: This state should never be reached.
1457            #  !!! cp (40);            #  !!! cp (40);
1458            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 1285  sub _get_next_token ($) { Line 1460  sub _get_next_token ($) {
1460              !!!cp (41);              !!!cp (41);
1461            #}            #}
1462          } else {          } else {
1463            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1464          }          }
1465          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1466          # reconsume          # reconsume
1467    
1468          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1469    
1470          redo A;          redo A;
1471        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1472          !!!cp (42);          !!!cp (42);
1473          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
1474          !!!next-input-character;          !!!next-input-character;
1475          redo A;          redo A;
1476        } else {        } else {
1477          !!!cp (44);          !!!cp (44);
1478          $self->{current_token}->{tag_name} .= chr $self->{next_char};          $self->{ct}->{tag_name} .= chr $self->{nc};
1479            # start tag or end tag            # start tag or end tag
1480          ## Stay in the state          ## Stay in the state
1481          !!!next-input-character;          !!!next-input-character;
1482          redo A;          redo A;
1483        }        }
1484      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1485        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  
1486          !!!cp (45);          !!!cp (45);
1487          ## Stay in the state          ## Stay in the state
1488          !!!next-input-character;          !!!next-input-character;
1489          redo A;          redo A;
1490        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1491          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1492            !!!cp (46);            !!!cp (46);
1493            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1494          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1495            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1496            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1497              !!!cp (47);              !!!cp (47);
1498              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1499            } else {            } else {
1500              !!!cp (48);              !!!cp (48);
1501            }            }
1502          } else {          } else {
1503            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1504          }          }
1505          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1506          !!!next-input-character;          !!!next-input-character;
1507    
1508          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1509    
1510          redo A;          redo A;
1511        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1512                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1513          !!!cp (49);          !!!cp (49);
1514          $self->{current_attribute}          $self->{ca}
1515              = {name => chr ($self->{next_char} + 0x0020),              = {name => chr ($self->{nc} + 0x0020),
1516                 value => '',                 value => '',
1517                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1518          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1519          !!!next-input-character;          !!!next-input-character;
1520          redo A;          redo A;
1521        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1522          !!!cp (50);          !!!cp (50);
1523          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
1524          !!!next-input-character;          !!!next-input-character;
1525          redo A;          redo A;
1526        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1527          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1528          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1529            !!!cp (52);            !!!cp (52);
1530            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1531          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1532            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1533            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1534              !!!cp (53);              !!!cp (53);
1535              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1536            } else {            } else {
1537              !!!cp (54);              !!!cp (54);
1538            }            }
1539          } else {          } else {
1540            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1541          }          }
1542          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1543          # reconsume          # reconsume
1544    
1545          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1546    
1547          redo A;          redo A;
1548        } else {        } else {
# Line 1379  sub _get_next_token ($) { Line 1550  sub _get_next_token ($) {
1550               0x0022 => 1, # "               0x0022 => 1, # "
1551               0x0027 => 1, # '               0x0027 => 1, # '
1552               0x003D => 1, # =               0x003D => 1, # =
1553              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1554            !!!cp (55);            !!!cp (55);
1555            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1556          } else {          } else {
1557            !!!cp (56);            !!!cp (56);
1558          }          }
1559          $self->{current_attribute}          $self->{ca}
1560              = {name => chr ($self->{next_char}),              = {name => chr ($self->{nc}),
1561                 value => '',                 value => '',
1562                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1563          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
# Line 1395  sub _get_next_token ($) { Line 1566  sub _get_next_token ($) {
1566        }        }
1567      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1568        my $before_leave = sub {        my $before_leave = sub {
1569          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1570              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1571            !!!cp (57);            !!!cp (57);
1572            !!!parse-error (type => 'duplicate attribute', text => $self->{current_attribute}->{name}, line => $self->{current_attribute}->{line}, column => $self->{current_attribute}->{column});            !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1573            ## Discard $self->{current_attribute} # MUST            ## Discard $self->{ca} # MUST
1574          } else {          } else {
1575            !!!cp (58);            !!!cp (58);
1576            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            $self->{ct}->{attributes}->{$self->{ca}->{name}}
1577              = $self->{current_attribute};              = $self->{ca};
1578          }          }
1579        }; # $before_leave        }; # $before_leave
1580    
1581        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  
1582          !!!cp (59);          !!!cp (59);
1583          $before_leave->();          $before_leave->();
1584          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1585          !!!next-input-character;          !!!next-input-character;
1586          redo A;          redo A;
1587        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1588          !!!cp (60);          !!!cp (60);
1589          $before_leave->();          $before_leave->();
1590          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1591          !!!next-input-character;          !!!next-input-character;
1592          redo A;          redo A;
1593        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1594          $before_leave->();          $before_leave->();
1595          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1596            !!!cp (61);            !!!cp (61);
1597            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1598          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1599            !!!cp (62);            !!!cp (62);
1600            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1601            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1602              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1603            }            }
1604          } else {          } else {
1605            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1606          }          }
1607          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1608          !!!next-input-character;          !!!next-input-character;
1609    
1610          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1611    
1612          redo A;          redo A;
1613        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1614                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1615          !!!cp (63);          !!!cp (63);
1616          $self->{current_attribute}->{name} .= chr ($self->{next_char} + 0x0020);          $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1617          ## Stay in the state          ## Stay in the state
1618          !!!next-input-character;          !!!next-input-character;
1619          redo A;          redo A;
1620        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1621          !!!cp (64);          !!!cp (64);
1622          $before_leave->();          $before_leave->();
1623          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
1624          !!!next-input-character;          !!!next-input-character;
1625          redo A;          redo A;
1626        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1627          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1628          $before_leave->();          $before_leave->();
1629          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1630            !!!cp (66);            !!!cp (66);
1631            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1632          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1633            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1634            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1635              !!!cp (67);              !!!cp (67);
1636              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1637            } else {            } else {
# Line 1472  sub _get_next_token ($) { Line 1639  sub _get_next_token ($) {
1639              !!!cp (68);              !!!cp (68);
1640            }            }
1641          } else {          } else {
1642            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1643          }          }
1644          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1645          # reconsume          # reconsume
1646    
1647          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1648    
1649          redo A;          redo A;
1650        } else {        } else {
1651          if ($self->{next_char} == 0x0022 or # "          if ($self->{nc} == 0x0022 or # "
1652              $self->{next_char} == 0x0027) { # '              $self->{nc} == 0x0027) { # '
1653            !!!cp (69);            !!!cp (69);
1654            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1655          } else {          } else {
1656            !!!cp (70);            !!!cp (70);
1657          }          }
1658          $self->{current_attribute}->{name} .= chr ($self->{next_char});          $self->{ca}->{name} .= chr ($self->{nc});
1659          ## Stay in the state          ## Stay in the state
1660          !!!next-input-character;          !!!next-input-character;
1661          redo A;          redo A;
1662        }        }
1663      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1664        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  
1665          !!!cp (71);          !!!cp (71);
1666          ## Stay in the state          ## Stay in the state
1667          !!!next-input-character;          !!!next-input-character;
1668          redo A;          redo A;
1669        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1670          !!!cp (72);          !!!cp (72);
1671          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1672          !!!next-input-character;          !!!next-input-character;
1673          redo A;          redo A;
1674        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1675          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1676            !!!cp (73);            !!!cp (73);
1677            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1678          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1679            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1680            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1681              !!!cp (74);              !!!cp (74);
1682              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1683            } else {            } else {
# Line 1522  sub _get_next_token ($) { Line 1685  sub _get_next_token ($) {
1685              !!!cp (75);              !!!cp (75);
1686            }            }
1687          } else {          } else {
1688            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1689          }          }
1690          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1691          !!!next-input-character;          !!!next-input-character;
1692    
1693          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1694    
1695          redo A;          redo A;
1696        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1697                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1698          !!!cp (76);          !!!cp (76);
1699          $self->{current_attribute}          $self->{ca}
1700              = {name => chr ($self->{next_char} + 0x0020),              = {name => chr ($self->{nc} + 0x0020),
1701                 value => '',                 value => '',
1702                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1703          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1704          !!!next-input-character;          !!!next-input-character;
1705          redo A;          redo A;
1706        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1707          !!!cp (77);          !!!cp (77);
1708          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
1709          !!!next-input-character;          !!!next-input-character;
1710          redo A;          redo A;
1711        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1712          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1713          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1714            !!!cp (79);            !!!cp (79);
1715            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1716          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1717            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1718            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1719              !!!cp (80);              !!!cp (80);
1720              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1721            } else {            } else {
# Line 1560  sub _get_next_token ($) { Line 1723  sub _get_next_token ($) {
1723              !!!cp (81);              !!!cp (81);
1724            }            }
1725          } else {          } else {
1726            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1727          }          }
1728          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1729          # reconsume          # reconsume
1730    
1731          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1732    
1733          redo A;          redo A;
1734        } else {        } else {
1735          if ($self->{next_char} == 0x0022 or # "          if ($self->{nc} == 0x0022 or # "
1736              $self->{next_char} == 0x0027) { # '              $self->{nc} == 0x0027) { # '
1737            !!!cp (78);            !!!cp (78);
1738            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1739          } else {          } else {
1740            !!!cp (82);            !!!cp (82);
1741          }          }
1742          $self->{current_attribute}          $self->{ca}
1743              = {name => chr ($self->{next_char}),              = {name => chr ($self->{nc}),
1744                 value => '',                 value => '',
1745                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1746          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
# Line 1585  sub _get_next_token ($) { Line 1748  sub _get_next_token ($) {
1748          redo A;                  redo A;        
1749        }        }
1750      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1751        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        
1752          !!!cp (83);          !!!cp (83);
1753          ## Stay in the state          ## Stay in the state
1754          !!!next-input-character;          !!!next-input-character;
1755          redo A;          redo A;
1756        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1757          !!!cp (84);          !!!cp (84);
1758          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1759          !!!next-input-character;          !!!next-input-character;
1760          redo A;          redo A;
1761        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1762          !!!cp (85);          !!!cp (85);
1763          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1764          ## reconsume          ## reconsume
1765          redo A;          redo A;
1766        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1767          !!!cp (86);          !!!cp (86);
1768          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1769          !!!next-input-character;          !!!next-input-character;
1770          redo A;          redo A;
1771        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1772          !!!parse-error (type => 'empty unquoted attribute value');          !!!parse-error (type => 'empty unquoted attribute value');
1773          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1774            !!!cp (87);            !!!cp (87);
1775            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1776          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1777            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1778            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1779              !!!cp (88);              !!!cp (88);
1780              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1781            } else {            } else {
# Line 1624  sub _get_next_token ($) { Line 1783  sub _get_next_token ($) {
1783              !!!cp (89);              !!!cp (89);
1784            }            }
1785          } else {          } else {
1786            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1787          }          }
1788          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1789          !!!next-input-character;          !!!next-input-character;
1790    
1791          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1792    
1793          redo A;          redo A;
1794        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1795          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1796          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1797            !!!cp (90);            !!!cp (90);
1798            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1799          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1800            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1801            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1802              !!!cp (91);              !!!cp (91);
1803              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1804            } else {            } else {
# Line 1647  sub _get_next_token ($) { Line 1806  sub _get_next_token ($) {
1806              !!!cp (92);              !!!cp (92);
1807            }            }
1808          } else {          } else {
1809            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1810          }          }
1811          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1812          ## reconsume          ## reconsume
1813    
1814          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1815    
1816          redo A;          redo A;
1817        } else {        } else {
1818          if ($self->{next_char} == 0x003D) { # =          if ($self->{nc} == 0x003D) { # =
1819            !!!cp (93);            !!!cp (93);
1820            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1821          } else {          } else {
1822            !!!cp (94);            !!!cp (94);
1823          }          }
1824          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1825          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1826          !!!next-input-character;          !!!next-input-character;
1827          redo A;          redo A;
1828        }        }
1829      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1830        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1831          !!!cp (95);          !!!cp (95);
1832          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1833          !!!next-input-character;          !!!next-input-character;
1834          redo A;          redo A;
1835        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1836          !!!cp (96);          !!!cp (96);
1837          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1838          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1839            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1840            ## implementation of the "consume a character reference" algorithm.
1841            $self->{prev_state} = $self->{state};
1842            $self->{entity_add} = 0x0022; # "
1843            $self->{state} = ENTITY_STATE;
1844          !!!next-input-character;          !!!next-input-character;
1845          redo A;          redo A;
1846        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1847          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1848          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1849            !!!cp (97);            !!!cp (97);
1850            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1851          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1852            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1853            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1854              !!!cp (98);              !!!cp (98);
1855              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1856            } else {            } else {
# Line 1694  sub _get_next_token ($) { Line 1858  sub _get_next_token ($) {
1858              !!!cp (99);              !!!cp (99);
1859            }            }
1860          } else {          } else {
1861            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1862          }          }
1863          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1864          ## reconsume          ## reconsume
1865    
1866          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1867    
1868          redo A;          redo A;
1869        } else {        } else {
1870          !!!cp (100);          !!!cp (100);
1871          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1872            $self->{read_until}->($self->{ca}->{value},
1873                                  q["&],
1874                                  length $self->{ca}->{value});
1875    
1876          ## Stay in the state          ## Stay in the state
1877          !!!next-input-character;          !!!next-input-character;
1878          redo A;          redo A;
1879        }        }
1880      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1881        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1882          !!!cp (101);          !!!cp (101);
1883          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1884          !!!next-input-character;          !!!next-input-character;
1885          redo A;          redo A;
1886        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1887          !!!cp (102);          !!!cp (102);
1888          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1889          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1890            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1891            ## implementation of the "consume a character reference" algorithm.
1892            $self->{entity_add} = 0x0027; # '
1893            $self->{prev_state} = $self->{state};
1894            $self->{state} = ENTITY_STATE;
1895          !!!next-input-character;          !!!next-input-character;
1896          redo A;          redo A;
1897        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1898          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1899          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1900            !!!cp (103);            !!!cp (103);
1901            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1902          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1903            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1904            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1905              !!!cp (104);              !!!cp (104);
1906              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1907            } else {            } else {
# Line 1736  sub _get_next_token ($) { Line 1909  sub _get_next_token ($) {
1909              !!!cp (105);              !!!cp (105);
1910            }            }
1911          } else {          } else {
1912            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1913          }          }
1914          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1915          ## reconsume          ## reconsume
1916    
1917          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1918    
1919          redo A;          redo A;
1920        } else {        } else {
1921          !!!cp (106);          !!!cp (106);
1922          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1923            $self->{read_until}->($self->{ca}->{value},
1924                                  q['&],
1925                                  length $self->{ca}->{value});
1926    
1927          ## Stay in the state          ## Stay in the state
1928          !!!next-input-character;          !!!next-input-character;
1929          redo A;          redo A;
1930        }        }
1931      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1932        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  
1933          !!!cp (107);          !!!cp (107);
1934          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1935          !!!next-input-character;          !!!next-input-character;
1936          redo A;          redo A;
1937        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1938          !!!cp (108);          !!!cp (108);
1939          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1940          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1941            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1942            ## implementation of the "consume a character reference" algorithm.
1943            $self->{entity_add} = -1;
1944            $self->{prev_state} = $self->{state};
1945            $self->{state} = ENTITY_STATE;
1946          !!!next-input-character;          !!!next-input-character;
1947          redo A;          redo A;
1948        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1949          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1950            !!!cp (109);            !!!cp (109);
1951            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1952          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1953            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1954            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1955              !!!cp (110);              !!!cp (110);
1956              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1957            } else {            } else {
# Line 1781  sub _get_next_token ($) { Line 1959  sub _get_next_token ($) {
1959              !!!cp (111);              !!!cp (111);
1960            }            }
1961          } else {          } else {
1962            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1963          }          }
1964          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1965          !!!next-input-character;          !!!next-input-character;
1966    
1967          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1968    
1969          redo A;          redo A;
1970        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1971          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1972          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1973            !!!cp (112);            !!!cp (112);
1974            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1975          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1976            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1977            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1978              !!!cp (113);              !!!cp (113);
1979              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1980            } else {            } else {
# Line 1804  sub _get_next_token ($) { Line 1982  sub _get_next_token ($) {
1982              !!!cp (114);              !!!cp (114);
1983            }            }
1984          } else {          } else {
1985            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1986          }          }
1987          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1988          ## reconsume          ## reconsume
1989    
1990          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1991    
1992          redo A;          redo A;
1993        } else {        } else {
# Line 1817  sub _get_next_token ($) { Line 1995  sub _get_next_token ($) {
1995               0x0022 => 1, # "               0x0022 => 1, # "
1996               0x0027 => 1, # '               0x0027 => 1, # '
1997               0x003D => 1, # =               0x003D => 1, # =
1998              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1999            !!!cp (115);            !!!cp (115);
2000            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
2001          } else {          } else {
2002            !!!cp (116);            !!!cp (116);
2003          }          }
2004          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
2005            $self->{read_until}->($self->{ca}->{value},
2006                                  q["'=& >],
2007                                  length $self->{ca}->{value});
2008    
2009          ## Stay in the state          ## Stay in the state
2010          !!!next-input-character;          !!!next-input-character;
2011          redo A;          redo A;
2012        }        }
     } 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;  
2013      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2014        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  
2015          !!!cp (118);          !!!cp (118);
2016          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2017          !!!next-input-character;          !!!next-input-character;
2018          redo A;          redo A;
2019        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2020          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2021            !!!cp (119);            !!!cp (119);
2022            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
2023          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2024            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2025            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2026              !!!cp (120);              !!!cp (120);
2027              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2028            } else {            } else {
# Line 1874  sub _get_next_token ($) { Line 2030  sub _get_next_token ($) {
2030              !!!cp (121);              !!!cp (121);
2031            }            }
2032          } else {          } else {
2033            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
2034          }          }
2035          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2036          !!!next-input-character;          !!!next-input-character;
2037    
2038          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2039    
2040          redo A;          redo A;
2041        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
2042          !!!cp (122);          !!!cp (122);
2043          $self->{state} = SELF_CLOSING_START_TAG_STATE;          $self->{state} = SELF_CLOSING_START_TAG_STATE;
2044          !!!next-input-character;          !!!next-input-character;
2045          redo A;          redo A;
2046        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2047          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
2048          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2049            !!!cp (122.3);            !!!cp (122.3);
2050            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
2051          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2052            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2053              !!!cp (122.1);              !!!cp (122.1);
2054              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2055            } else {            } else {
# Line 1901  sub _get_next_token ($) { Line 2057  sub _get_next_token ($) {
2057              !!!cp (122.2);              !!!cp (122.2);
2058            }            }
2059          } else {          } else {
2060            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
2061          }          }
2062          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2063          ## Reconsume.          ## Reconsume.
2064          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2065          redo A;          redo A;
2066        } else {        } else {
2067          !!!cp ('124.1');          !!!cp ('124.1');
# Line 1915  sub _get_next_token ($) { Line 2071  sub _get_next_token ($) {
2071          redo A;          redo A;
2072        }        }
2073      } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {      } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2074        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2075          if ($self->{current_token}->{type} == END_TAG_TOKEN) {          if ($self->{ct}->{type} == END_TAG_TOKEN) {
2076            !!!cp ('124.2');            !!!cp ('124.2');
2077            !!!parse-error (type => 'nestc', token => $self->{current_token});            !!!parse-error (type => 'nestc', token => $self->{ct});
2078            ## TODO: Different type than slash in start tag            ## TODO: Different type than slash in start tag
2079            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2080            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2081              !!!cp ('124.4');              !!!cp ('124.4');
2082              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2083            } else {            } else {
# Line 1936  sub _get_next_token ($) { Line 2092  sub _get_next_token ($) {
2092          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2093          !!!next-input-character;          !!!next-input-character;
2094    
2095          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2096    
2097          redo A;          redo A;
2098        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2099          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
2100          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2101            !!!cp (124.7);            !!!cp (124.7);
2102            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
2103          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2104            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2105              !!!cp (124.5);              !!!cp (124.5);
2106              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2107            } else {            } else {
# Line 1953  sub _get_next_token ($) { Line 2109  sub _get_next_token ($) {
2109              !!!cp (124.6);              !!!cp (124.6);
2110            }            }
2111          } else {          } else {
2112            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
2113          }          }
2114          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2115          ## Reconsume.          ## Reconsume.
2116          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2117          redo A;          redo A;
2118        } else {        } else {
2119          !!!cp ('124.4');          !!!cp ('124.4');
# Line 1969  sub _get_next_token ($) { Line 2125  sub _get_next_token ($) {
2125        }        }
2126      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2127        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
         
       ## NOTE: Set by the previous state  
       #my $token = {type => COMMENT_TOKEN, data => ''};  
2128    
2129        BC: {        ## NOTE: Unlike spec's "bogus comment state", this implementation
2130          if ($self->{next_char} == 0x003E) { # >        ## consumes characters one-by-one basis.
2131            !!!cp (124);        
2132            $self->{state} = DATA_STATE;        if ($self->{nc} == 0x003E) { # >
2133            !!!next-input-character;          !!!cp (124);
2134            $self->{state} = DATA_STATE;
2135            !!!emit ($self->{current_token}); # comment          !!!next-input-character;
   
           redo A;  
         } elsif ($self->{next_char} == -1) {  
           !!!cp (125);  
           $self->{state} = DATA_STATE;  
           ## reconsume  
2136    
2137            !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2138            redo A;
2139          } elsif ($self->{nc} == -1) {
2140            !!!cp (125);
2141            $self->{state} = DATA_STATE;
2142            ## reconsume
2143    
2144            redo A;          !!!emit ($self->{ct}); # comment
2145          } else {          redo A;
2146            !!!cp (126);        } else {
2147            $self->{current_token}->{data} .= chr ($self->{next_char}); # comment          !!!cp (126);
2148            !!!next-input-character;          $self->{ct}->{data} .= chr ($self->{nc}); # comment
2149            redo BC;          $self->{read_until}->($self->{ct}->{data},
2150          }                                q[>],
2151        } # BC                                length $self->{ct}->{data});
2152    
2153        die "$0: _get_next_token: unexpected case [BC]";          ## Stay in the state.
2154            !!!next-input-character;
2155            redo A;
2156          }
2157      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2158        ## (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};  
2159                
2160        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2161            !!!cp (133);
2162            $self->{state} = MD_HYPHEN_STATE;
2163          !!!next-input-character;          !!!next-input-character;
2164          push @next_char, $self->{next_char};          redo A;
2165          if ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x0044 or # D
2166            !!!cp (127);                 $self->{nc} == 0x0064) { # d
2167            $self->{current_token} = {type => COMMENT_TOKEN, data => '',          ## ASCII case-insensitive.
2168                                      line => $l, column => $c,          !!!cp (130);
2169                                     };          $self->{state} = MD_DOCTYPE_STATE;
2170            $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  
2171          !!!next-input-character;          !!!next-input-character;
2172          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);  
         }  
2173        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2174                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2175                 $self->{next_char} == 0x005B) { # [                 $self->{nc} == 0x005B) { # [
2176            !!!cp (135.4);                
2177            $self->{state} = MD_CDATA_STATE;
2178            $self->{s_kwd} = '[';
2179          !!!next-input-character;          !!!next-input-character;
2180          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);  
         }  
2181        } else {        } else {
2182          !!!cp (136);          !!!cp (136);
2183        }        }
2184    
2185        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment',
2186        $self->{next_char} = shift @next_char;                        line => $self->{line_prev},
2187        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2188          ## Reconsume.
2189        $self->{state} = BOGUS_COMMENT_STATE;        $self->{state} = BOGUS_COMMENT_STATE;
2190        $self->{current_token} = {type => COMMENT_TOKEN, data => '',        $self->{ct} = {type => COMMENT_TOKEN, data => '',
2191                                  line => $l, column => $c,                                  line => $self->{line_prev},
2192                                    column => $self->{column_prev} - 1,
2193                                 };                                 };
2194        redo A;        redo A;
2195              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2196        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2197        ## 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);
2198            $self->{ct} = {type => COMMENT_TOKEN, data => '',
2199                                      line => $self->{line_prev},
2200                                      column => $self->{column_prev} - 2,
2201                                     };
2202            $self->{state} = COMMENT_START_STATE;
2203            !!!next-input-character;
2204            redo A;
2205          } else {
2206            !!!cp (128);
2207            !!!parse-error (type => 'bogus comment',
2208                            line => $self->{line_prev},
2209                            column => $self->{column_prev} - 2);
2210            $self->{state} = BOGUS_COMMENT_STATE;
2211            ## Reconsume.
2212            $self->{ct} = {type => COMMENT_TOKEN,
2213                                      data => '-',
2214                                      line => $self->{line_prev},
2215                                      column => $self->{column_prev} - 2,
2216                                     };
2217            redo A;
2218          }
2219        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2220          ## ASCII case-insensitive.
2221          if ($self->{nc} == [
2222                undef,
2223                0x004F, # O
2224                0x0043, # C
2225                0x0054, # T
2226                0x0059, # Y
2227                0x0050, # P
2228              ]->[length $self->{s_kwd}] or
2229              $self->{nc} == [
2230                undef,
2231                0x006F, # o
2232                0x0063, # c
2233                0x0074, # t
2234                0x0079, # y
2235                0x0070, # p
2236              ]->[length $self->{s_kwd}]) {
2237            !!!cp (131);
2238            ## Stay in the state.
2239            $self->{s_kwd} .= chr $self->{nc};
2240            !!!next-input-character;
2241            redo A;
2242          } elsif ((length $self->{s_kwd}) == 6 and
2243                   ($self->{nc} == 0x0045 or # E
2244                    $self->{nc} == 0x0065)) { # e
2245            !!!cp (129);
2246            $self->{state} = DOCTYPE_STATE;
2247            $self->{ct} = {type => DOCTYPE_TOKEN,
2248                                      quirks => 1,
2249                                      line => $self->{line_prev},
2250                                      column => $self->{column_prev} - 7,
2251                                     };
2252            !!!next-input-character;
2253            redo A;
2254          } else {
2255            !!!cp (132);        
2256            !!!parse-error (type => 'bogus comment',
2257                            line => $self->{line_prev},
2258                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2259            $self->{state} = BOGUS_COMMENT_STATE;
2260            ## Reconsume.
2261            $self->{ct} = {type => COMMENT_TOKEN,
2262                                      data => $self->{s_kwd},
2263                                      line => $self->{line_prev},
2264                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2265                                     };
2266            redo A;
2267          }
2268        } elsif ($self->{state} == MD_CDATA_STATE) {
2269          if ($self->{nc} == {
2270                '[' => 0x0043, # C
2271                '[C' => 0x0044, # D
2272                '[CD' => 0x0041, # A
2273                '[CDA' => 0x0054, # T
2274                '[CDAT' => 0x0041, # A
2275              }->{$self->{s_kwd}}) {
2276            !!!cp (135.1);
2277            ## Stay in the state.
2278            $self->{s_kwd} .= chr $self->{nc};
2279            !!!next-input-character;
2280            redo A;
2281          } elsif ($self->{s_kwd} eq '[CDATA' and
2282                   $self->{nc} == 0x005B) { # [
2283            !!!cp (135.2);
2284            $self->{ct} = {type => CHARACTER_TOKEN,
2285                                      data => '',
2286                                      line => $self->{line_prev},
2287                                      column => $self->{column_prev} - 7};
2288            $self->{state} = CDATA_SECTION_STATE;
2289            !!!next-input-character;
2290            redo A;
2291          } else {
2292            !!!cp (135.3);
2293            !!!parse-error (type => 'bogus comment',
2294                            line => $self->{line_prev},
2295                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2296            $self->{state} = BOGUS_COMMENT_STATE;
2297            ## Reconsume.
2298            $self->{ct} = {type => COMMENT_TOKEN,
2299                                      data => $self->{s_kwd},
2300                                      line => $self->{line_prev},
2301                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2302                                     };
2303            redo A;
2304          }
2305      } elsif ($self->{state} == COMMENT_START_STATE) {      } elsif ($self->{state} == COMMENT_START_STATE) {
2306        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2307          !!!cp (137);          !!!cp (137);
2308          $self->{state} = COMMENT_START_DASH_STATE;          $self->{state} = COMMENT_START_DASH_STATE;
2309          !!!next-input-character;          !!!next-input-character;
2310          redo A;          redo A;
2311        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2312          !!!cp (138);          !!!cp (138);
2313          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2314          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2315          !!!next-input-character;          !!!next-input-character;
2316    
2317          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2318    
2319          redo A;          redo A;
2320        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2321          !!!cp (139);          !!!cp (139);
2322          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2323          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2324          ## reconsume          ## reconsume
2325    
2326          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2327    
2328          redo A;          redo A;
2329        } else {        } else {
2330          !!!cp (140);          !!!cp (140);
2331          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2332              .= chr ($self->{next_char});              .= chr ($self->{nc});
2333          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2334          !!!next-input-character;          !!!next-input-character;
2335          redo A;          redo A;
2336        }        }
2337      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2338        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2339          !!!cp (141);          !!!cp (141);
2340          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2341          !!!next-input-character;          !!!next-input-character;
2342          redo A;          redo A;
2343        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2344          !!!cp (142);          !!!cp (142);
2345          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2346          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2347          !!!next-input-character;          !!!next-input-character;
2348    
2349          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2350    
2351          redo A;          redo A;
2352        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2353          !!!cp (143);          !!!cp (143);
2354          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2355          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2356          ## reconsume          ## reconsume
2357    
2358          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2359    
2360          redo A;          redo A;
2361        } else {        } else {
2362          !!!cp (144);          !!!cp (144);
2363          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2364              .= '-' . chr ($self->{next_char});              .= '-' . chr ($self->{nc});
2365          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2366          !!!next-input-character;          !!!next-input-character;
2367          redo A;          redo A;
2368        }        }
2369      } elsif ($self->{state} == COMMENT_STATE) {      } elsif ($self->{state} == COMMENT_STATE) {
2370        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2371          !!!cp (145);          !!!cp (145);
2372          $self->{state} = COMMENT_END_DASH_STATE;          $self->{state} = COMMENT_END_DASH_STATE;
2373          !!!next-input-character;          !!!next-input-character;
2374          redo A;          redo A;
2375        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2376          !!!cp (146);          !!!cp (146);
2377          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2378          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2379          ## reconsume          ## reconsume
2380    
2381          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2382    
2383          redo A;          redo A;
2384        } else {        } else {
2385          !!!cp (147);          !!!cp (147);
2386          $self->{current_token}->{data} .= chr ($self->{next_char}); # comment          $self->{ct}->{data} .= chr ($self->{nc}); # comment
2387            $self->{read_until}->($self->{ct}->{data},
2388                                  q[-],
2389                                  length $self->{ct}->{data});
2390    
2391          ## Stay in the state          ## Stay in the state
2392          !!!next-input-character;          !!!next-input-character;
2393          redo A;          redo A;
2394        }        }
2395      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2396        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2397          !!!cp (148);          !!!cp (148);
2398          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2399          !!!next-input-character;          !!!next-input-character;
2400          redo A;          redo A;
2401        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2402          !!!cp (149);          !!!cp (149);
2403          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2404          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2405          ## reconsume          ## reconsume
2406    
2407          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2408    
2409          redo A;          redo A;
2410        } else {        } else {
2411          !!!cp (150);          !!!cp (150);
2412          $self->{current_token}->{data} .= '-' . chr ($self->{next_char}); # comment          $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2413          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2414          !!!next-input-character;          !!!next-input-character;
2415          redo A;          redo A;
2416        }        }
2417      } elsif ($self->{state} == COMMENT_END_STATE) {      } elsif ($self->{state} == COMMENT_END_STATE) {
2418        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2419          !!!cp (151);          !!!cp (151);
2420          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2421          !!!next-input-character;          !!!next-input-character;
2422    
2423          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2424    
2425          redo A;          redo A;
2426        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2427          !!!cp (152);          !!!cp (152);
2428          !!!parse-error (type => 'dash in comment',          !!!parse-error (type => 'dash in comment',
2429                          line => $self->{line_prev},                          line => $self->{line_prev},
2430                          column => $self->{column_prev});                          column => $self->{column_prev});
2431          $self->{current_token}->{data} .= '-'; # comment          $self->{ct}->{data} .= '-'; # comment
2432          ## Stay in the state          ## Stay in the state
2433          !!!next-input-character;          !!!next-input-character;
2434          redo A;          redo A;
2435        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2436          !!!cp (153);          !!!cp (153);
2437          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2438          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2439          ## reconsume          ## reconsume
2440    
2441          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2442    
2443          redo A;          redo A;
2444        } else {        } else {
# Line 2272  sub _get_next_token ($) { Line 2446  sub _get_next_token ($) {
2446          !!!parse-error (type => 'dash in comment',          !!!parse-error (type => 'dash in comment',
2447                          line => $self->{line_prev},                          line => $self->{line_prev},
2448                          column => $self->{column_prev});                          column => $self->{column_prev});
2449          $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment          $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2450          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2451          !!!next-input-character;          !!!next-input-character;
2452          redo A;          redo A;
2453        }        }
2454      } elsif ($self->{state} == DOCTYPE_STATE) {      } elsif ($self->{state} == DOCTYPE_STATE) {
2455        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  
2456          !!!cp (155);          !!!cp (155);
2457          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2458          !!!next-input-character;          !!!next-input-character;
# Line 2295  sub _get_next_token ($) { Line 2465  sub _get_next_token ($) {
2465          redo A;          redo A;
2466        }        }
2467      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2468        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  
2469          !!!cp (157);          !!!cp (157);
2470          ## Stay in the state          ## Stay in the state
2471          !!!next-input-character;          !!!next-input-character;
2472          redo A;          redo A;
2473        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2474          !!!cp (158);          !!!cp (158);
2475          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2476          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2477          !!!next-input-character;          !!!next-input-character;
2478    
2479          !!!emit ($self->{current_token}); # DOCTYPE (quirks)          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2480    
2481          redo A;          redo A;
2482        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2483          !!!cp (159);          !!!cp (159);
2484          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2485          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2486          ## reconsume          ## reconsume
2487    
2488          !!!emit ($self->{current_token}); # DOCTYPE (quirks)          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2489    
2490          redo A;          redo A;
2491        } else {        } else {
2492          !!!cp (160);          !!!cp (160);
2493          $self->{current_token}->{name} = chr $self->{next_char};          $self->{ct}->{name} = chr $self->{nc};
2494          delete $self->{current_token}->{quirks};          delete $self->{ct}->{quirks};
 ## ISSUE: "Set the token's name name to the" in the spec  
2495          $self->{state} = DOCTYPE_NAME_STATE;          $self->{state} = DOCTYPE_NAME_STATE;
2496          !!!next-input-character;          !!!next-input-character;
2497          redo A;          redo A;
2498        }        }
2499      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2500  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2501        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  
2502          !!!cp (161);          !!!cp (161);
2503          $self->{state} = AFTER_DOCTYPE_NAME_STATE;          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
2504          !!!next-input-character;          !!!next-input-character;
2505          redo A;          redo A;
2506        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2507          !!!cp (162);          !!!cp (162);
2508          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2509          !!!next-input-character;          !!!next-input-character;
2510    
2511          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2512    
2513          redo A;          redo A;
2514        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2515          !!!cp (163);          !!!cp (163);
2516          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2517          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2518          ## reconsume          ## reconsume
2519    
2520          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2521          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2522    
2523          redo A;          redo A;
2524        } else {        } else {
2525          !!!cp (164);          !!!cp (164);
2526          $self->{current_token}->{name}          $self->{ct}->{name}
2527            .= chr ($self->{next_char}); # DOCTYPE            .= chr ($self->{nc}); # DOCTYPE
2528          ## Stay in the state          ## Stay in the state
2529          !!!next-input-character;          !!!next-input-character;
2530          redo A;          redo A;
2531        }        }
2532      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2533        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  
2534          !!!cp (165);          !!!cp (165);
2535          ## Stay in the state          ## Stay in the state
2536          !!!next-input-character;          !!!next-input-character;
2537          redo A;          redo A;
2538        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2539          !!!cp (166);          !!!cp (166);
2540          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2541          !!!next-input-character;          !!!next-input-character;
2542    
2543          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2544    
2545          redo A;          redo A;
2546        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2547          !!!cp (167);          !!!cp (167);
2548          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2549          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2550          ## reconsume          ## reconsume
2551    
2552          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2553          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2554    
2555          redo A;          redo A;
2556        } elsif ($self->{next_char} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2557                 $self->{next_char} == 0x0070) { # p                 $self->{nc} == 0x0070) { # p
2558            $self->{state} = PUBLIC_STATE;
2559            $self->{s_kwd} = chr $self->{nc};
2560          !!!next-input-character;          !!!next-input-character;
2561          if ($self->{next_char} == 0x0055 or # U          redo A;
2562              $self->{next_char} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2563            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2564            if ($self->{next_char} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2565                $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  
2566          !!!next-input-character;          !!!next-input-character;
2567          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);  
         }  
   
         #  
2568        } else {        } else {
2569          !!!cp (180);          !!!cp (180);
2570            !!!parse-error (type => 'string after DOCTYPE name');
2571            $self->{ct}->{quirks} = 1;
2572    
2573            $self->{state} = BOGUS_DOCTYPE_STATE;
2574          !!!next-input-character;          !!!next-input-character;
2575          #          redo A;
2576        }        }
2577        } elsif ($self->{state} == PUBLIC_STATE) {
2578          ## ASCII case-insensitive
2579          if ($self->{nc} == [
2580                undef,
2581                0x0055, # U
2582                0x0042, # B
2583                0x004C, # L
2584                0x0049, # I
2585              ]->[length $self->{s_kwd}] or
2586              $self->{nc} == [
2587                undef,
2588                0x0075, # u
2589                0x0062, # b
2590                0x006C, # l
2591                0x0069, # i
2592              ]->[length $self->{s_kwd}]) {
2593            !!!cp (175);
2594            ## Stay in the state.
2595            $self->{s_kwd} .= chr $self->{nc};
2596            !!!next-input-character;
2597            redo A;
2598          } elsif ((length $self->{s_kwd}) == 5 and
2599                   ($self->{nc} == 0x0043 or # C
2600                    $self->{nc} == 0x0063)) { # c
2601            !!!cp (168);
2602            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2603            !!!next-input-character;
2604            redo A;
2605          } else {
2606            !!!cp (169);
2607            !!!parse-error (type => 'string after DOCTYPE name',
2608                            line => $self->{line_prev},
2609                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2610            $self->{ct}->{quirks} = 1;
2611    
2612        !!!parse-error (type => 'string after DOCTYPE name');          $self->{state} = BOGUS_DOCTYPE_STATE;
2613        $self->{current_token}->{quirks} = 1;          ## Reconsume.
2614            redo A;
2615          }
2616        } elsif ($self->{state} == SYSTEM_STATE) {
2617          ## ASCII case-insensitive
2618          if ($self->{nc} == [
2619                undef,
2620                0x0059, # Y
2621                0x0053, # S
2622                0x0054, # T
2623                0x0045, # E
2624              ]->[length $self->{s_kwd}] or
2625              $self->{nc} == [
2626                undef,
2627                0x0079, # y
2628                0x0073, # s
2629                0x0074, # t
2630                0x0065, # e
2631              ]->[length $self->{s_kwd}]) {
2632            !!!cp (170);
2633            ## Stay in the state.
2634            $self->{s_kwd} .= chr $self->{nc};
2635            !!!next-input-character;
2636            redo A;
2637          } elsif ((length $self->{s_kwd}) == 5 and
2638                   ($self->{nc} == 0x004D or # M
2639                    $self->{nc} == 0x006D)) { # m
2640            !!!cp (171);
2641            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2642            !!!next-input-character;
2643            redo A;
2644          } else {
2645            !!!cp (172);
2646            !!!parse-error (type => 'string after DOCTYPE name',
2647                            line => $self->{line_prev},
2648                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2649            $self->{ct}->{quirks} = 1;
2650    
2651        $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2652        # next-input-character is already done          ## Reconsume.
2653        redo A;          redo A;
2654          }
2655      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2656        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}}) {  
2657          !!!cp (181);          !!!cp (181);
2658          ## Stay in the state          ## Stay in the state
2659          !!!next-input-character;          !!!next-input-character;
2660          redo A;          redo A;
2661        } elsif ($self->{next_char} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2662          !!!cp (182);          !!!cp (182);
2663          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2664          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2665          !!!next-input-character;          !!!next-input-character;
2666          redo A;          redo A;
2667        } elsif ($self->{next_char} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2668          !!!cp (183);          !!!cp (183);
2669          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2670          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2671          !!!next-input-character;          !!!next-input-character;
2672          redo A;          redo A;
2673        } elsif ($self->{next_char} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2674          !!!cp (184);          !!!cp (184);
2675          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2676    
2677          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2678          !!!next-input-character;          !!!next-input-character;
2679    
2680          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2681          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2682    
2683          redo A;          redo A;
2684        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2685          !!!cp (185);          !!!cp (185);
2686          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2687    
2688          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2689          ## reconsume          ## reconsume
2690    
2691          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2692          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2693    
2694          redo A;          redo A;
2695        } else {        } else {
2696          !!!cp (186);          !!!cp (186);
2697          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2698          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2699    
2700          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2701          !!!next-input-character;          !!!next-input-character;
2702          redo A;          redo A;
2703        }        }
2704      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2705        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2706          !!!cp (187);          !!!cp (187);
2707          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2708          !!!next-input-character;          !!!next-input-character;
2709          redo A;          redo A;
2710        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2711          !!!cp (188);          !!!cp (188);
2712          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2713    
2714          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2715          !!!next-input-character;          !!!next-input-character;
2716    
2717          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2718          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2719    
2720          redo A;          redo A;
2721        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2722          !!!cp (189);          !!!cp (189);
2723          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2724    
2725          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2726          ## reconsume          ## reconsume
2727    
2728          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2729          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2730    
2731          redo A;          redo A;
2732        } else {        } else {
2733          !!!cp (190);          !!!cp (190);
2734          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2735              .= chr $self->{next_char};              .= chr $self->{nc};
2736            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2737                                  length $self->{ct}->{pubid});
2738    
2739          ## Stay in the state          ## Stay in the state
2740          !!!next-input-character;          !!!next-input-character;
2741          redo A;          redo A;
2742        }        }
2743      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2744        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2745          !!!cp (191);          !!!cp (191);
2746          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2747          !!!next-input-character;          !!!next-input-character;
2748          redo A;          redo A;
2749        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2750          !!!cp (192);          !!!cp (192);
2751          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2752    
2753          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2754          !!!next-input-character;          !!!next-input-character;
2755    
2756          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2757          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2758    
2759          redo A;          redo A;
2760        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2761          !!!cp (193);          !!!cp (193);
2762          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2763    
2764          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2765          ## reconsume          ## reconsume
2766    
2767          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2768          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2769    
2770          redo A;          redo A;
2771        } else {        } else {
2772          !!!cp (194);          !!!cp (194);
2773          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2774              .= chr $self->{next_char};              .= chr $self->{nc};
2775            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2776                                  length $self->{ct}->{pubid});
2777    
2778          ## Stay in the state          ## Stay in the state
2779          !!!next-input-character;          !!!next-input-character;
2780          redo A;          redo A;
2781        }        }
2782      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2783        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}}) {  
2784          !!!cp (195);          !!!cp (195);
2785          ## Stay in the state          ## Stay in the state
2786          !!!next-input-character;          !!!next-input-character;
2787          redo A;          redo A;
2788        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2789          !!!cp (196);          !!!cp (196);
2790          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2791          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2792          !!!next-input-character;          !!!next-input-character;
2793          redo A;          redo A;
2794        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2795          !!!cp (197);          !!!cp (197);
2796          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2797          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2798          !!!next-input-character;          !!!next-input-character;
2799          redo A;          redo A;
2800        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2801          !!!cp (198);          !!!cp (198);
2802          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2803          !!!next-input-character;          !!!next-input-character;
2804    
2805          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2806    
2807          redo A;          redo A;
2808        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2809          !!!cp (199);          !!!cp (199);
2810          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2811    
2812          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2813          ## reconsume          ## reconsume
2814    
2815          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2816          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2817    
2818          redo A;          redo A;
2819        } else {        } else {
2820          !!!cp (200);          !!!cp (200);
2821          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2822          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2823    
2824          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2825          !!!next-input-character;          !!!next-input-character;
2826          redo A;          redo A;
2827        }        }
2828      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2829        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}}) {  
2830          !!!cp (201);          !!!cp (201);
2831          ## Stay in the state          ## Stay in the state
2832          !!!next-input-character;          !!!next-input-character;
2833          redo A;          redo A;
2834        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2835          !!!cp (202);          !!!cp (202);
2836          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2837          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2838          !!!next-input-character;          !!!next-input-character;
2839          redo A;          redo A;
2840        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2841          !!!cp (203);          !!!cp (203);
2842          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2843          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2844          !!!next-input-character;          !!!next-input-character;
2845          redo A;          redo A;
2846        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2847          !!!cp (204);          !!!cp (204);
2848          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2849          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2850          !!!next-input-character;          !!!next-input-character;
2851    
2852          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2853          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2854    
2855          redo A;          redo A;
2856        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2857          !!!cp (205);          !!!cp (205);
2858          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2859    
2860          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2861          ## reconsume          ## reconsume
2862    
2863          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2864          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2865    
2866          redo A;          redo A;
2867        } else {        } else {
2868          !!!cp (206);          !!!cp (206);
2869          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2870          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2871    
2872          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2873          !!!next-input-character;          !!!next-input-character;
2874          redo A;          redo A;
2875        }        }
2876      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2877        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2878          !!!cp (207);          !!!cp (207);
2879          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2880          !!!next-input-character;          !!!next-input-character;
2881          redo A;          redo A;
2882        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2883          !!!cp (208);          !!!cp (208);
2884          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2885    
2886          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2887          !!!next-input-character;          !!!next-input-character;
2888    
2889          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2890          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2891    
2892          redo A;          redo A;
2893        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2894          !!!cp (209);          !!!cp (209);
2895          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2896    
2897          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2898          ## reconsume          ## reconsume
2899    
2900          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2901          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2902    
2903          redo A;          redo A;
2904        } else {        } else {
2905          !!!cp (210);          !!!cp (210);
2906          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2907              .= chr $self->{next_char};              .= chr $self->{nc};
2908            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2909                                  length $self->{ct}->{sysid});
2910    
2911          ## Stay in the state          ## Stay in the state
2912          !!!next-input-character;          !!!next-input-character;
2913          redo A;          redo A;
2914        }        }
2915      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2916        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2917          !!!cp (211);          !!!cp (211);
2918          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2919          !!!next-input-character;          !!!next-input-character;
2920          redo A;          redo A;
2921        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2922          !!!cp (212);          !!!cp (212);
2923          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2924    
2925          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2926          !!!next-input-character;          !!!next-input-character;
2927    
2928          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2929          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2930    
2931          redo A;          redo A;
2932        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2933          !!!cp (213);          !!!cp (213);
2934          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2935    
2936          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2937          ## reconsume          ## reconsume
2938    
2939          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2940          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2941    
2942          redo A;          redo A;
2943        } else {        } else {
2944          !!!cp (214);          !!!cp (214);
2945          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2946              .= chr $self->{next_char};              .= chr $self->{nc};
2947            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2948                                  length $self->{ct}->{sysid});
2949    
2950          ## Stay in the state          ## Stay in the state
2951          !!!next-input-character;          !!!next-input-character;
2952          redo A;          redo A;
2953        }        }
2954      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2955        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}}) {  
2956          !!!cp (215);          !!!cp (215);
2957          ## Stay in the state          ## Stay in the state
2958          !!!next-input-character;          !!!next-input-character;
2959          redo A;          redo A;
2960        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2961          !!!cp (216);          !!!cp (216);
2962          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2963          !!!next-input-character;          !!!next-input-character;
2964    
2965          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2966    
2967          redo A;          redo A;
2968        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2969          !!!cp (217);          !!!cp (217);
2970          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2971          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2972          ## reconsume          ## reconsume
2973    
2974          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2975          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2976    
2977          redo A;          redo A;
2978        } else {        } else {
2979          !!!cp (218);          !!!cp (218);
2980          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2981          #$self->{current_token}->{quirks} = 1;          #$self->{ct}->{quirks} = 1;
2982    
2983          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2984          !!!next-input-character;          !!!next-input-character;
2985          redo A;          redo A;
2986        }        }
2987      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2988        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2989          !!!cp (219);          !!!cp (219);
2990          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2991          !!!next-input-character;          !!!next-input-character;
2992    
2993          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2994    
2995          redo A;          redo A;
2996        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2997          !!!cp (220);          !!!cp (220);
         !!!parse-error (type => 'unclosed DOCTYPE');  
2998          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2999          ## reconsume          ## reconsume
3000    
3001          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
3002    
3003          redo A;          redo A;
3004        } else {        } else {
3005          !!!cp (221);          !!!cp (221);
3006            my $s = '';
3007            $self->{read_until}->($s, q[>], 0);
3008    
3009          ## Stay in the state          ## Stay in the state
3010          !!!next-input-character;          !!!next-input-character;
3011          redo A;          redo A;
3012        }        }
3013      } elsif ($self->{state} == CDATA_BLOCK_STATE) {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
3014        my $s = '';        ## NOTE: "CDATA section state" in the state is jointly implemented
3015          ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3016          ## and |CDATA_SECTION_MSE2_STATE|.
3017                
3018        my ($l, $c) = ($self->{line}, $self->{column});        if ($self->{nc} == 0x005D) { # ]
3019            !!!cp (221.1);
3020        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};  
3021          !!!next-input-character;          !!!next-input-character;
3022        } # CS          redo A;
3023          } elsif ($self->{nc} == -1) {
3024            $self->{state} = DATA_STATE;
3025            !!!next-input-character;
3026            if (length $self->{ct}->{data}) { # character
3027              !!!cp (221.2);
3028              !!!emit ($self->{ct}); # character
3029            } else {
3030              !!!cp (221.3);
3031              ## No token to emit. $self->{ct} is discarded.
3032            }        
3033            redo A;
3034          } else {
3035            !!!cp (221.4);
3036            $self->{ct}->{data} .= chr $self->{nc};
3037            $self->{read_until}->($self->{ct}->{data},
3038                                  q<]>,
3039                                  length $self->{ct}->{data});
3040    
3041        $self->{state} = DATA_STATE;          ## Stay in the state.
3042        ## next-input-character done or EOF, which is reconsumed.          !!!next-input-character;
3043            redo A;
3044          }
3045    
3046        if (length $s) {        ## ISSUE: "text tokens" in spec.
3047        } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3048          if ($self->{nc} == 0x005D) { # ]
3049            !!!cp (221.5);
3050            $self->{state} = CDATA_SECTION_MSE2_STATE;
3051            !!!next-input-character;
3052            redo A;
3053          } else {
3054          !!!cp (221.6);          !!!cp (221.6);
3055          !!!emit ({type => CHARACTER_TOKEN, data => $s,          $self->{ct}->{data} .= ']';
3056                    line => $l, column => $c});          $self->{state} = CDATA_SECTION_STATE;
3057            ## Reconsume.
3058            redo A;
3059          }
3060        } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3061          if ($self->{nc} == 0x003E) { # >
3062            $self->{state} = DATA_STATE;
3063            !!!next-input-character;
3064            if (length $self->{ct}->{data}) { # character
3065              !!!cp (221.7);
3066              !!!emit ($self->{ct}); # character
3067            } else {
3068              !!!cp (221.8);
3069              ## No token to emit. $self->{ct} is discarded.
3070            }
3071            redo A;
3072          } elsif ($self->{nc} == 0x005D) { # ]
3073            !!!cp (221.9); # character
3074            $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3075            ## Stay in the state.
3076            !!!next-input-character;
3077            redo A;
3078        } else {        } else {
3079          !!!cp (221.7);          !!!cp (221.11);
3080            $self->{ct}->{data} .= ']]'; # character
3081            $self->{state} = CDATA_SECTION_STATE;
3082            ## Reconsume.
3083            redo A;
3084          }
3085        } elsif ($self->{state} == ENTITY_STATE) {
3086          if ($is_space->{$self->{nc}} or
3087              {
3088                0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3089                $self->{entity_add} => 1,
3090              }->{$self->{nc}}) {
3091            !!!cp (1001);
3092            ## Don't consume
3093            ## No error
3094            ## Return nothing.
3095            #
3096          } elsif ($self->{nc} == 0x0023) { # #
3097            !!!cp (999);
3098            $self->{state} = ENTITY_HASH_STATE;
3099            $self->{s_kwd} = '#';
3100            !!!next-input-character;
3101            redo A;
3102          } elsif ((0x0041 <= $self->{nc} and
3103                    $self->{nc} <= 0x005A) or # A..Z
3104                   (0x0061 <= $self->{nc} and
3105                    $self->{nc} <= 0x007A)) { # a..z
3106            !!!cp (998);
3107            require Whatpm::_NamedEntityList;
3108            $self->{state} = ENTITY_NAME_STATE;
3109            $self->{s_kwd} = chr $self->{nc};
3110            $self->{entity__value} = $self->{s_kwd};
3111            $self->{entity__match} = 0;
3112            !!!next-input-character;
3113            redo A;
3114          } else {
3115            !!!cp (1027);
3116            !!!parse-error (type => 'bare ero');
3117            ## Return nothing.
3118            #
3119        }        }
3120    
3121        redo A;        ## NOTE: No character is consumed by the "consume a character
3122          ## reference" algorithm.  In other word, there is an "&" character
3123        ## ISSUE: "text tokens" in spec.        ## that does not introduce a character reference, which would be
3124        ## TODO: Streaming support        ## appended to the parent element or the attribute value in later
3125      } else {        ## process of the tokenizer.
3126        die "$0: $self->{state}: Unknown state";  
3127      }        if ($self->{prev_state} == DATA_STATE) {
3128    } # A            !!!cp (997);
3129            $self->{state} = $self->{prev_state};
3130    die "$0: _get_next_token: unexpected case";          ## Reconsume.
3131  } # _get_next_token          !!!emit ({type => CHARACTER_TOKEN, data => '&',
3132                      line => $self->{line_prev},
3133  sub _tokenize_attempt_to_consume_an_entity ($$$) {                    column => $self->{column_prev},
3134    my ($self, $in_attr, $additional) = @_;                   });
3135            redo A;
3136    my ($l, $c) = ($self->{line_prev}, $self->{column_prev});        } else {
3137            !!!cp (996);
3138            $self->{ca}->{value} .= '&';
3139            $self->{state} = $self->{prev_state};
3140            ## Reconsume.
3141            redo A;
3142          }
3143        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3144          if ($self->{nc} == 0x0078 or # x
3145              $self->{nc} == 0x0058) { # X
3146            !!!cp (995);
3147            $self->{state} = HEXREF_X_STATE;
3148            $self->{s_kwd} .= chr $self->{nc};
3149            !!!next-input-character;
3150            redo A;
3151          } elsif (0x0030 <= $self->{nc} and
3152                   $self->{nc} <= 0x0039) { # 0..9
3153            !!!cp (994);
3154            $self->{state} = NCR_NUM_STATE;
3155            $self->{s_kwd} = $self->{nc} - 0x0030;
3156            !!!next-input-character;
3157            redo A;
3158          } else {
3159            !!!parse-error (type => 'bare nero',
3160                            line => $self->{line_prev},
3161                            column => $self->{column_prev} - 1);
3162    
3163    if ({          ## NOTE: According to the spec algorithm, nothing is returned,
3164         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,          ## and then "&#" is appended to the parent element or the attribute
3165         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR          ## value in the later processing.
3166         $additional => 1,  
3167        }->{$self->{next_char}}) {          if ($self->{prev_state} == DATA_STATE) {
3168      !!!cp (1001);            !!!cp (1019);
3169      ## Don't consume            $self->{state} = $self->{prev_state};
3170      ## No error            ## Reconsume.
3171      return undef;            !!!emit ({type => CHARACTER_TOKEN,
3172    } elsif ($self->{next_char} == 0x0023) { # #                      data => '&#',
3173      !!!next-input-character;                      line => $self->{line_prev},
3174      if ($self->{next_char} == 0x0078 or # x                      column => $self->{column_prev} - 1,
3175          $self->{next_char} == 0x0058) { # X                     });
3176        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;  
3177          } else {          } else {
3178            !!!cp (1007);            !!!cp (993);
3179            !!!parse-error (type => 'no refc', line => $l, column => $c);            $self->{ca}->{value} .= '&#';
3180              $self->{state} = $self->{prev_state};
3181              ## Reconsume.
3182              redo A;
3183          }          }
3184          }
3185          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {      } elsif ($self->{state} == NCR_NUM_STATE) {
3186            !!!cp (1008);        if (0x0030 <= $self->{nc} and
3187            !!!parse-error (type => 'invalid character reference',            $self->{nc} <= 0x0039) { # 0..9
                           text => (sprintf 'U+%04X', $code),  
                           line => $l, column => $c);  
           $code = 0xFFFD;  
         } elsif ($code > 0x10FFFF) {  
           !!!cp (1009);  
           !!!parse-error (type => 'invalid character reference',  
                           text => (sprintf 'U-%08X', $code),  
                           line => $l, column => $c);  
           $code = 0xFFFD;  
         } elsif ($code == 0x000D) {  
           !!!cp (1010);  
           !!!parse-error (type => 'CR character reference', line => $l, column => $c);  
           $code = 0x000A;  
         } elsif (0x80 <= $code and $code <= 0x9F) {  
           !!!cp (1011);  
           !!!parse-error (type => 'C1 character reference', text => (sprintf 'U+%04X', $code), line => $l, column => $c);  
           $code = $c1_entity_char->{$code};  
         }  
   
         return {type => CHARACTER_TOKEN, data => chr $code,  
                 has_reference => 1,  
                 line => $l, column => $c,  
                };  
       } # X  
     } elsif (0x0030 <= $self->{next_char} and  
              $self->{next_char} <= 0x0039) { # 0..9  
       my $code = $self->{next_char} - 0x0030;  
       !!!next-input-character;  
         
       while (0x0030 <= $self->{next_char} and  
                 $self->{next_char} <= 0x0039) { # 0..9  
3188          !!!cp (1012);          !!!cp (1012);
3189          $code *= 10;          $self->{s_kwd} *= 10;
3190          $code += $self->{next_char} - 0x0030;          $self->{s_kwd} += $self->{nc} - 0x0030;
3191                    
3192            ## Stay in the state.
3193          !!!next-input-character;          !!!next-input-character;
3194        }          redo A;
3195          } elsif ($self->{nc} == 0x003B) { # ;
       if ($self->{next_char} == 0x003B) { # ;  
3196          !!!cp (1013);          !!!cp (1013);
3197          !!!next-input-character;          !!!next-input-character;
3198            #
3199        } else {        } else {
3200          !!!cp (1014);          !!!cp (1014);
3201          !!!parse-error (type => 'no refc', line => $l, column => $c);          !!!parse-error (type => 'no refc');
3202            ## Reconsume.
3203            #
3204        }        }
3205    
3206        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3207          my $l = $self->{line_prev};
3208          my $c = $self->{column_prev};
3209          if ($charref_map->{$code}) {
3210          !!!cp (1015);          !!!cp (1015);
3211          !!!parse-error (type => 'invalid character reference',          !!!parse-error (type => 'invalid character reference',
3212                          text => (sprintf 'U+%04X', $code),                          text => (sprintf 'U+%04X', $code),
3213                          line => $l, column => $c);                          line => $l, column => $c);
3214          $code = 0xFFFD;          $code = $charref_map->{$code};
3215        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3216          !!!cp (1016);          !!!cp (1016);
3217          !!!parse-error (type => 'invalid character reference',          !!!parse-error (type => 'invalid character reference',
3218                          text => (sprintf 'U-%08X', $code),                          text => (sprintf 'U-%08X', $code),
3219                          line => $l, column => $c);                          line => $l, column => $c);
3220          $code = 0xFFFD;          $code = 0xFFFD;
3221        } elsif ($code == 0x000D) {        }
3222          !!!cp (1017);  
3223          !!!parse-error (type => 'CR character reference',        if ($self->{prev_state} == DATA_STATE) {
3224                          line => $l, column => $c);          !!!cp (992);
3225          $code = 0x000A;          $self->{state} = $self->{prev_state};
3226        } elsif (0x80 <= $code and $code <= 0x9F) {          ## Reconsume.
3227          !!!cp (1018);          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3228          !!!parse-error (type => 'C1 character reference',                    line => $l, column => $c,
3229                     });
3230            redo A;
3231          } else {
3232            !!!cp (991);
3233            $self->{ca}->{value} .= chr $code;
3234            $self->{ca}->{has_reference} = 1;
3235            $self->{state} = $self->{prev_state};
3236            ## Reconsume.
3237            redo A;
3238          }
3239        } elsif ($self->{state} == HEXREF_X_STATE) {
3240          if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3241              (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3242              (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3243            # 0..9, A..F, a..f
3244            !!!cp (990);
3245            $self->{state} = HEXREF_HEX_STATE;
3246            $self->{s_kwd} = 0;
3247            ## Reconsume.
3248            redo A;
3249          } else {
3250            !!!parse-error (type => 'bare hcro',
3251                            line => $self->{line_prev},
3252                            column => $self->{column_prev} - 2);
3253    
3254            ## NOTE: According to the spec algorithm, nothing is returned,
3255            ## and then "&#" followed by "X" or "x" is appended to the parent
3256            ## element or the attribute value in the later processing.
3257    
3258            if ($self->{prev_state} == DATA_STATE) {
3259              !!!cp (1005);
3260              $self->{state} = $self->{prev_state};
3261              ## Reconsume.
3262              !!!emit ({type => CHARACTER_TOKEN,
3263                        data => '&' . $self->{s_kwd},
3264                        line => $self->{line_prev},
3265                        column => $self->{column_prev} - length $self->{s_kwd},
3266                       });
3267              redo A;
3268            } else {
3269              !!!cp (989);
3270              $self->{ca}->{value} .= '&' . $self->{s_kwd};
3271              $self->{state} = $self->{prev_state};
3272              ## Reconsume.
3273              redo A;
3274            }
3275          }
3276        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3277          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3278            # 0..9
3279            !!!cp (1002);
3280            $self->{s_kwd} *= 0x10;
3281            $self->{s_kwd} += $self->{nc} - 0x0030;
3282            ## Stay in the state.
3283            !!!next-input-character;
3284            redo A;
3285          } elsif (0x0061 <= $self->{nc} and
3286                   $self->{nc} <= 0x0066) { # a..f
3287            !!!cp (1003);
3288            $self->{s_kwd} *= 0x10;
3289            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3290            ## Stay in the state.
3291            !!!next-input-character;
3292            redo A;
3293          } elsif (0x0041 <= $self->{nc} and
3294                   $self->{nc} <= 0x0046) { # A..F
3295            !!!cp (1004);
3296            $self->{s_kwd} *= 0x10;
3297            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3298            ## Stay in the state.
3299            !!!next-input-character;
3300            redo A;
3301          } elsif ($self->{nc} == 0x003B) { # ;
3302            !!!cp (1006);
3303            !!!next-input-character;
3304            #
3305          } else {
3306            !!!cp (1007);
3307            !!!parse-error (type => 'no refc',
3308                            line => $self->{line},
3309                            column => $self->{column});
3310            ## Reconsume.
3311            #
3312          }
3313    
3314          my $code = $self->{s_kwd};
3315          my $l = $self->{line_prev};
3316          my $c = $self->{column_prev};
3317          if ($charref_map->{$code}) {
3318            !!!cp (1008);
3319            !!!parse-error (type => 'invalid character reference',
3320                          text => (sprintf 'U+%04X', $code),                          text => (sprintf 'U+%04X', $code),
3321                          line => $l, column => $c);                          line => $l, column => $c);
3322          $code = $c1_entity_char->{$code};          $code = $charref_map->{$code};
3323          } elsif ($code > 0x10FFFF) {
3324            !!!cp (1009);
3325            !!!parse-error (type => 'invalid character reference',
3326                            text => (sprintf 'U-%08X', $code),
3327                            line => $l, column => $c);
3328            $code = 0xFFFD;
3329        }        }
3330          
3331        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1,        if ($self->{prev_state} == DATA_STATE) {
3332                line => $l, column => $c,          !!!cp (988);
3333               };          $self->{state} = $self->{prev_state};
3334      } else {          ## Reconsume.
3335        !!!cp (1019);          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3336        !!!parse-error (type => 'bare nero', line => $l, column => $c);                    line => $l, column => $c,
3337        !!!back-next-input-character ($self->{next_char});                   });
3338        $self->{next_char} = 0x0023; # #          redo A;
3339        return undef;        } else {
3340      }          !!!cp (987);
3341    } elsif ((0x0041 <= $self->{next_char} and          $self->{ca}->{value} .= chr $code;
3342              $self->{next_char} <= 0x005A) or          $self->{ca}->{has_reference} = 1;
3343             (0x0061 <= $self->{next_char} and          $self->{state} = $self->{prev_state};
3344              $self->{next_char} <= 0x007A)) {          ## Reconsume.
3345      my $entity_name = chr $self->{next_char};          redo A;
3346      !!!next-input-character;        }
3347        } elsif ($self->{state} == ENTITY_NAME_STATE) {
3348      my $value = $entity_name;        if (length $self->{s_kwd} < 30 and
3349      my $match = 0;            ## NOTE: Some number greater than the maximum length of entity name
3350      require Whatpm::_NamedEntityList;            ((0x0041 <= $self->{nc} and # a
3351      our $EntityChar;              $self->{nc} <= 0x005A) or # x
3352               (0x0061 <= $self->{nc} and # a
3353      while (length $entity_name < 30 and              $self->{nc} <= 0x007A) or # z
3354             ## NOTE: Some number greater than the maximum length of entity name             (0x0030 <= $self->{nc} and # 0
3355             ((0x0041 <= $self->{next_char} and # a              $self->{nc} <= 0x0039) or # 9
3356               $self->{next_char} <= 0x005A) or # x             $self->{nc} == 0x003B)) { # ;
3357              (0x0061 <= $self->{next_char} and # a          our $EntityChar;
3358               $self->{next_char} <= 0x007A) or # z          $self->{s_kwd} .= chr $self->{nc};
3359              (0x0030 <= $self->{next_char} and # 0          if (defined $EntityChar->{$self->{s_kwd}}) {
3360               $self->{next_char} <= 0x0039) or # 9            if ($self->{nc} == 0x003B) { # ;
3361              $self->{next_char} == 0x003B)) { # ;              !!!cp (1020);
3362        $entity_name .= chr $self->{next_char};              $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3363        if (defined $EntityChar->{$entity_name}) {              $self->{entity__match} = 1;
3364          if ($self->{next_char} == 0x003B) { # ;              !!!next-input-character;
3365            !!!cp (1020);              #
3366            $value = $EntityChar->{$entity_name};            } else {
3367            $match = 1;              !!!cp (1021);
3368            !!!next-input-character;              $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3369            last;              $self->{entity__match} = -1;
3370                ## Stay in the state.
3371                !!!next-input-character;
3372                redo A;
3373              }
3374          } else {          } else {
3375            !!!cp (1021);            !!!cp (1022);
3376            $value = $EntityChar->{$entity_name};            $self->{entity__value} .= chr $self->{nc};
3377            $match = -1;            $self->{entity__match} *= 2;
3378              ## Stay in the state.
3379            !!!next-input-character;            !!!next-input-character;
3380              redo A;
3381            }
3382          }
3383    
3384          my $data;
3385          my $has_ref;
3386          if ($self->{entity__match} > 0) {
3387            !!!cp (1023);
3388            $data = $self->{entity__value};
3389            $has_ref = 1;
3390            #
3391          } elsif ($self->{entity__match} < 0) {
3392            !!!parse-error (type => 'no refc');
3393            if ($self->{prev_state} != DATA_STATE and # in attribute
3394                $self->{entity__match} < -1) {
3395              !!!cp (1024);
3396              $data = '&' . $self->{s_kwd};
3397              #
3398            } else {
3399              !!!cp (1025);
3400              $data = $self->{entity__value};
3401              $has_ref = 1;
3402              #
3403          }          }
3404        } else {        } else {
3405          !!!cp (1022);          !!!cp (1026);
3406          $value .= chr $self->{next_char};          !!!parse-error (type => 'bare ero',
3407          $match *= 2;                          line => $self->{line_prev},
3408          !!!next-input-character;                          column => $self->{column_prev} - length $self->{s_kwd});
3409            $data = '&' . $self->{s_kwd};
3410            #
3411        }        }
3412      }    
3413              ## NOTE: In these cases, when a character reference is found,
3414      if ($match > 0) {        ## it is consumed and a character token is returned, or, otherwise,
3415        !!!cp (1023);        ## nothing is consumed and returned, according to the spec algorithm.
3416        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,        ## In this implementation, anything that has been examined by the
3417                line => $l, column => $c,        ## tokenizer is appended to the parent element or the attribute value
3418               };        ## as string, either literal string when no character reference or
3419      } elsif ($match < 0) {        ## entity-replaced string otherwise, in this stage, since any characters
3420        !!!parse-error (type => 'no refc', line => $l, column => $c);        ## that would not be consumed are appended in the data state or in an
3421        if ($in_attr and $match < -1) {        ## appropriate attribute value state anyway.
3422          !!!cp (1024);  
3423          return {type => CHARACTER_TOKEN, data => '&'.$entity_name,        if ($self->{prev_state} == DATA_STATE) {
3424                  line => $l, column => $c,          !!!cp (986);
3425                 };          $self->{state} = $self->{prev_state};
3426        } else {          ## Reconsume.
3427          !!!cp (1025);          !!!emit ({type => CHARACTER_TOKEN,
3428          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,                    data => $data,
3429                  line => $l, column => $c,                    line => $self->{line_prev},
3430                 };                    column => $self->{column_prev} + 1 - length $self->{s_kwd},
3431                     });
3432            redo A;
3433          } else {
3434            !!!cp (985);
3435            $self->{ca}->{value} .= $data;
3436            $self->{ca}->{has_reference} = 1 if $has_ref;
3437            $self->{state} = $self->{prev_state};
3438            ## Reconsume.
3439            redo A;
3440        }        }
3441      } else {      } else {
3442        !!!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,  
              };  
3443      }      }
3444    } else {    } # A  
3445      !!!cp (1027);  
3446      ## no characters are consumed    die "$0: _get_next_token: unexpected case";
3447      !!!parse-error (type => 'bare ero', line => $l, column => $c);  } # _get_next_token
     return undef;  
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3448    
3449  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3450    my $self = shift;    my $self = shift;
# Line 3148  sub _construct_tree ($) { Line 3474  sub _construct_tree ($) {
3474    ## When an interactive UA render the $self->{document} available    ## When an interactive UA render the $self->{document} available
3475    ## to the user, or when it begin accepting user input, are    ## to the user, or when it begin accepting user input, are
3476    ## not defined.    ## not defined.
   
   ## Append a character: collect it and all subsequent consecutive  
   ## characters and insert one Text node whose data is concatenation  
   ## of all those characters. # MUST  
3477        
3478    !!!next-token;    !!!next-token;
3479    
3480    undef $self->{form_element};    undef $self->{form_element};
3481    undef $self->{head_element};    undef $self->{head_element};
3482      undef $self->{head_element_inserted};
3483    $self->{open_elements} = [];    $self->{open_elements} = [];
3484    undef $self->{inner_html_node};    undef $self->{inner_html_node};
3485      undef $self->{ignore_newline};
3486    
3487    ## NOTE: The "initial" insertion mode.    ## NOTE: The "initial" insertion mode.
3488    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
# Line 3185  sub _tree_construction_initial ($) { Line 3509  sub _tree_construction_initial ($) {
3509        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3510        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3511        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3512            defined $token->{system_identifier}) {            defined $token->{sysid}) {
3513          !!!cp ('t1');          !!!cp ('t1');
3514          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
3515        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3516          !!!cp ('t2');          !!!cp ('t2');
3517          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
3518        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3519          if ($token->{public_identifier} eq 'XSLT-compat') {          if ($token->{pubid} eq 'XSLT-compat') {
3520            !!!cp ('t1.2');            !!!cp ('t1.2');
3521            !!!parse-error (type => 'XSLT-compat', token => $token,            !!!parse-error (type => 'XSLT-compat', token => $token,
3522                            level => $self->{level}->{should});                            level => $self->{level}->{should});
# Line 3208  sub _tree_construction_initial ($) { Line 3532  sub _tree_construction_initial ($) {
3532          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3533        ## NOTE: Default value for both |public_id| and |system_id| attributes        ## NOTE: Default value for both |public_id| and |system_id| attributes
3534        ## 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.
3535        $doctype->public_id ($token->{public_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3536            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};  
3537        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3538        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3539        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
# Line 3219  sub _tree_construction_initial ($) { Line 3541  sub _tree_construction_initial ($) {
3541        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3542          !!!cp ('t4');          !!!cp ('t4');
3543          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3544        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3545          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3546          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3547          my $prefix = [          my $prefix = [
3548            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
# Line 3294  sub _tree_construction_initial ($) { Line 3616  sub _tree_construction_initial ($) {
3616            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3617          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3618                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3619            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3620              !!!cp ('t6');              !!!cp ('t6');
3621              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3622            } else {            } else {
# Line 3311  sub _tree_construction_initial ($) { Line 3633  sub _tree_construction_initial ($) {
3633        } else {        } else {
3634          !!!cp ('t10');          !!!cp ('t10');
3635        }        }
3636        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3637          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3638          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3639          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") {
3640            ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is            ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
# Line 3342  sub _tree_construction_initial ($) { Line 3664  sub _tree_construction_initial ($) {
3664        !!!ack-later;        !!!ack-later;
3665        return;        return;
3666      } elsif ($token->{type} == CHARACTER_TOKEN) {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3667        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3668          ## Ignore the token          ## Ignore the token
3669    
3670          unless (length $token->{data}) {          unless (length $token->{data}) {
# Line 3399  sub _tree_construction_root_element ($) Line 3721  sub _tree_construction_root_element ($)
3721          !!!next-token;          !!!next-token;
3722          redo B;          redo B;
3723        } elsif ($token->{type} == CHARACTER_TOKEN) {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3724          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3725            ## Ignore the token.            ## Ignore the token.
3726    
3727            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 3466  sub _tree_construction_root_element ($) Line 3788  sub _tree_construction_root_element ($)
3788      ## NOTE: Reprocess the token.      ## NOTE: Reprocess the token.
3789      !!!ack-later;      !!!ack-later;
3790      return; ## Go to the "before head" insertion mode.      return; ## Go to the "before head" insertion mode.
   
     ## ISSUE: There is an issue in the spec  
3791    } # B    } # B
3792    
3793    die "$0: _tree_construction_root_element: This should never be reached";    die "$0: _tree_construction_root_element: This should never be reached";
# Line 3503  sub _reset_insertion_mode ($) { Line 3823  sub _reset_insertion_mode ($) {
3823          ## SVG elements.  Currently the HTML syntax supports only MathML and          ## SVG elements.  Currently the HTML syntax supports only MathML and
3824          ## SVG elements as foreigners.          ## SVG elements as foreigners.
3825          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3826        } elsif ($node->[1] & TABLE_CELL_EL) {        } elsif ($node->[1] == TABLE_CELL_EL) {
3827          if ($last) {          if ($last) {
3828            !!!cp ('t28.2');            !!!cp ('t28.2');
3829            #            #
# Line 3532  sub _reset_insertion_mode ($) { Line 3852  sub _reset_insertion_mode ($) {
3852        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3853                
3854        ## Step 15        ## Step 15
3855        if ($node->[1] & HTML_EL) {        if ($node->[1] == HTML_EL) {
3856          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3857            !!!cp ('t29');            !!!cp ('t29');
3858            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
# Line 3664  sub _tree_construction_main ($) { Line 3984  sub _tree_construction_main ($) {
3984    
3985      ## Step 1      ## Step 1
3986      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
3987      my $el;      !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
     !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);  
3988    
3989      ## Step 2      ## Step 2
     $insert->($el);  
   
     ## Step 3  
3990      $self->{content_model} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
3991      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
3992    
3993      ## Step 4      ## Step 3, 4
3994      my $text = '';      $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
     !!!nack ('t40.1');  
     !!!next-token;  
     while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing  
       !!!cp ('t40');  
       $text .= $token->{data};  
       !!!next-token;  
     }  
   
     ## Step 5  
     if (length $text) {  
       !!!cp ('t41');  
       my $text = $self->{document}->create_text_node ($text);  
       $el->append_child ($text);  
     }  
   
     ## Step 6  
     $self->{content_model} = PCDATA_CONTENT_MODEL;  
3995    
3996      ## Step 7      !!!nack ('t40.1');
     if ($token->{type} == END_TAG_TOKEN and  
         $token->{tag_name} eq $start_tag_name) {  
       !!!cp ('t42');  
       ## Ignore the token  
     } else {  
       ## NOTE: An end-of-file token.  
       if ($content_model_flag == CDATA_CONTENT_MODEL) {  
         !!!cp ('t43');  
         !!!parse-error (type => 'in CDATA:#eof', token => $token);  
       } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {  
         !!!cp ('t44');  
         !!!parse-error (type => 'in RCDATA:#eof', token => $token);  
       } else {  
         die "$0: $content_model_flag in parse_rcdata";  
       }  
     }  
3997      !!!next-token;      !!!next-token;
3998    }; # $parse_rcdata    }; # $parse_rcdata
3999    
4000    my $script_start_tag = sub () {    my $script_start_tag = sub () {
4001        ## Step 1
4002      my $script_el;      my $script_el;
4003      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
4004    
4005        ## Step 2
4006      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
4007    
4008        ## Step 3
4009        ## TODO: Mark as "already executed", if ...
4010    
4011        ## Step 4
4012        $insert->($script_el);
4013    
4014        ## ISSUE: $script_el is not put into the stack
4015        push @{$self->{open_elements}}, [$script_el, $el_category->{script}];
4016    
4017        ## Step 5
4018      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
4019      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
       
     my $text = '';  
     !!!nack ('t45.1');  
     !!!next-token;  
     while ($token->{type} == CHARACTER_TOKEN) {  
       !!!cp ('t45');  
       $text .= $token->{data};  
       !!!next-token;  
     } # stop if non-character token or tokenizer stops tokenising  
     if (length $text) {  
       !!!cp ('t46');  
       $script_el->manakai_append_text ($text);  
     }  
                 
     $self->{content_model} = PCDATA_CONTENT_MODEL;  
4020    
4021      if ($token->{type} == END_TAG_TOKEN and      ## Step 6-7
4022          $token->{tag_name} eq 'script') {      $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
       !!!cp ('t47');  
       ## Ignore the token  
     } else {  
       !!!cp ('t48');  
       !!!parse-error (type => 'in CDATA:#eof', token => $token);  
       ## ISSUE: And ignore?  
       ## TODO: mark as "already executed"  
     }  
       
     if (defined $self->{inner_html_node}) {  
       !!!cp ('t49');  
       ## TODO: mark as "already executed"  
     } else {  
       !!!cp ('t50');  
       ## TODO: $old_insertion_point = current insertion point  
       ## TODO: insertion point = just before the next input character  
4023    
4024        $insert->($script_el);      !!!nack ('t40.2');
         
       ## TODO: insertion point = $old_insertion_point (might be "undefined")  
         
       ## TODO: if there is a script that will execute as soon as the parser resume, then...  
     }  
       
4025      !!!next-token;      !!!next-token;
4026    }; # $script_start_tag    }; # $script_start_tag
4027    
4028    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4029    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4030      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
4031    my $open_tables = [[$self->{open_elements}->[0]->[0]]];    my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4032    
4033    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
# Line 3852  sub _tree_construction_main ($) { Line 4112  sub _tree_construction_main ($) {
4112            !!!cp ('t59');            !!!cp ('t59');
4113            $furthest_block = $node;            $furthest_block = $node;
4114            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
4115              ## NOTE: The topmost (eldest) node.
4116          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
4117            !!!cp ('t60');            !!!cp ('t60');
4118            last OE;            last OE;
# Line 3938  sub _tree_construction_main ($) { Line 4199  sub _tree_construction_main ($) {
4199          my $foster_parent_element;          my $foster_parent_element;
4200          my $next_sibling;          my $next_sibling;
4201          OE: for (reverse 0..$#{$self->{open_elements}}) {          OE: for (reverse 0..$#{$self->{open_elements}}) {
4202            if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {            if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
4203                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4204                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4205                                 !!!cp ('t65.1');                                 !!!cp ('t65.1');
# Line 3998  sub _tree_construction_main ($) { Line 4259  sub _tree_construction_main ($) {
4259            $i = $_;            $i = $_;
4260          }          }
4261        } # OE        } # OE
4262        splice @{$self->{open_elements}}, $i + 1, 1, $clone;        splice @{$self->{open_elements}}, $i + 1, 0, $clone;
4263                
4264        ## Step 14        ## Step 14
4265        redo FET;        redo FET;
# Line 4016  sub _tree_construction_main ($) { Line 4277  sub _tree_construction_main ($) {
4277        my $foster_parent_element;        my $foster_parent_element;
4278        my $next_sibling;        my $next_sibling;
4279        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4280          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {          if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
4281                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4282                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4283                                 !!!cp ('t70');                                 !!!cp ('t70');
# Line 4041  sub _tree_construction_main ($) { Line 4302  sub _tree_construction_main ($) {
4302      }      }
4303    }; # $insert_to_foster    }; # $insert_to_foster
4304    
4305      ## NOTE: Insert a character (MUST): When a character is inserted, if
4306      ## the last node that was inserted by the parser is a Text node and
4307      ## the character has to be inserted after that node, then the
4308      ## character is appended to the Text node.  However, if any other
4309      ## node is inserted by the parser, then a new Text node is created
4310      ## and the character is appended as that Text node.  If I'm not
4311      ## wrong, for a parser with scripting disabled, there are only two
4312      ## cases where this occurs.  One is the case where an element node
4313      ## is inserted to the |head| element.  This is covered by using the
4314      ## |$self->{head_element_inserted}| flag.  Another is the case where
4315      ## an element or comment is inserted into the |table| subtree while
4316      ## foster parenting happens.  This is covered by using the [2] flag
4317      ## of the |$open_tables| structure.  All other cases are handled
4318      ## simply by calling |manakai_append_text| method.
4319    
4320      ## TODO: |<body><script>document.write("a<br>");
4321      ## document.body.removeChild (document.body.lastChild);
4322      ## document.write ("b")</script>|
4323    
4324    B: while (1) {    B: while (1) {
4325      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
4326        !!!cp ('t73');        !!!cp ('t73');
# Line 4088  sub _tree_construction_main ($) { Line 4368  sub _tree_construction_main ($) {
4368        } else {        } else {
4369          !!!cp ('t87');          !!!cp ('t87');
4370          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
4371            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
4372        }        }
4373        !!!next-token;        !!!next-token;
4374        next B;        next B;
4375        } elsif ($self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
4376          if ($token->{type} == CHARACTER_TOKEN) {
4377            $token->{data} =~ s/^\x0A// if $self->{ignore_newline};
4378            delete $self->{ignore_newline};
4379    
4380            if (length $token->{data}) {
4381              !!!cp ('t43');
4382              $self->{open_elements}->[-1]->[0]->manakai_append_text
4383                  ($token->{data});
4384            } else {
4385              !!!cp ('t43.1');
4386            }
4387            !!!next-token;
4388            next B;
4389          } elsif ($token->{type} == END_TAG_TOKEN) {
4390            delete $self->{ignore_newline};
4391    
4392            if ($token->{tag_name} eq 'script') {
4393              !!!cp ('t50');
4394              
4395              ## Para 1-2
4396              my $script = pop @{$self->{open_elements}};
4397              
4398              ## Para 3
4399              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4400    
4401              ## Para 4
4402              ## TODO: $old_insertion_point = $current_insertion_point;
4403              ## TODO: $current_insertion_point = just before $self->{nc};
4404    
4405              ## Para 5
4406              ## TODO: Run the $script->[0].
4407    
4408              ## Para 6
4409              ## TODO: $current_insertion_point = $old_insertion_point;
4410    
4411              ## Para 7
4412              ## TODO: if ($pending_external_script) {
4413                ## TODO: ...
4414              ## TODO: }
4415    
4416              !!!next-token;
4417              next B;
4418            } else {
4419              !!!cp ('t42');
4420    
4421              pop @{$self->{open_elements}};
4422    
4423              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4424              !!!next-token;
4425              next B;
4426            }
4427          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4428            delete $self->{ignore_newline};
4429    
4430            !!!cp ('t44');
4431            !!!parse-error (type => 'not closed',
4432                            text => $self->{open_elements}->[-1]->[0]
4433                                ->manakai_local_name,
4434                            token => $token);
4435    
4436            #if ($self->{open_elements}->[-1]->[1] == SCRIPT_EL) {
4437            #  ## TODO: Mark as "already executed"
4438            #}
4439    
4440            pop @{$self->{open_elements}};
4441    
4442            $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4443            ## Reprocess.
4444            next B;
4445          } else {
4446            die "$0: $token->{type}: In CDATA/RCDATA: Unknown token type";        
4447          }
4448      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4449        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4450          !!!cp ('t87.1');          !!!cp ('t87.1');
# Line 4102  sub _tree_construction_main ($) { Line 4456  sub _tree_construction_main ($) {
4456               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4457              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4458              ($token->{tag_name} eq 'svg' and              ($token->{tag_name} eq 'svg' and
4459               $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {               $self->{open_elements}->[-1]->[1] == MML_AXML_EL)) {
4460            ## NOTE: "using the rules for secondary insertion mode"then"continue"            ## NOTE: "using the rules for secondary insertion mode"then"continue"
4461            !!!cp ('t87.2');            !!!cp ('t87.2');
4462            #            #
# Line 4203  sub _tree_construction_main ($) { Line 4557  sub _tree_construction_main ($) {
4557          pop @{$self->{open_elements}}          pop @{$self->{open_elements}}
4558              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4559    
4560            ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
4561    
4562          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4563          ## Reprocess.          ## Reprocess.
4564          next B;          next B;
# Line 4213  sub _tree_construction_main ($) { Line 4569  sub _tree_construction_main ($) {
4569    
4570      if ($self->{insertion_mode} & HEAD_IMS) {      if ($self->{insertion_mode} & HEAD_IMS) {
4571        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4572          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4573            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4574              !!!cp ('t88.2');              if ($self->{head_element_inserted}) {
4575              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                !!!cp ('t88.3');
4576                  $self->{open_elements}->[-1]->[0]->append_child
4577                    ($self->{document}->create_text_node ($1));
4578                  delete $self->{head_element_inserted};
4579                  ## NOTE: |</head> <link> |
4580                  #
4581                } else {
4582                  !!!cp ('t88.2');
4583                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4584                  ## NOTE: |</head> &#x20;|
4585                  #
4586                }
4587            } else {            } else {
4588              !!!cp ('t88.1');              !!!cp ('t88.1');
4589              ## Ignore the token.              ## Ignore the token.
4590              !!!next-token;              #
             next B;  
4591            }            }
4592            unless (length $token->{data}) {            unless (length $token->{data}) {
4593              !!!cp ('t88');              !!!cp ('t88');
4594              !!!next-token;              !!!next-token;
4595              next B;              next B;
4596            }            }
4597    ## TODO: set $token->{column} appropriately
4598          }          }
4599    
4600          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
# Line 4312  sub _tree_construction_main ($) { Line 4679  sub _tree_construction_main ($) {
4679            !!!cp ('t97');            !!!cp ('t97');
4680          }          }
4681    
4682              if ($token->{tag_name} eq 'base') {          if ($token->{tag_name} eq 'base') {
4683                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4684                  !!!cp ('t98');              !!!cp ('t98');
4685                  ## As if </noscript>              ## As if </noscript>
4686                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4687                  !!!parse-error (type => 'in noscript', text => 'base',              !!!parse-error (type => 'in noscript', text => 'base',
4688                                  token => $token);                              token => $token);
4689                            
4690                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
4691                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
4692                } else {            } else {
4693                  !!!cp ('t99');              !!!cp ('t99');
4694                }            }
4695    
4696                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4697                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4698                  !!!cp ('t100');              !!!cp ('t100');
4699                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
4700                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
4701                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
4702                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
4703                } else {              $self->{head_element_inserted} = 1;
4704                  !!!cp ('t101');            } else {
4705                }              !!!cp ('t101');
4706                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            }
4707                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4708                pop @{$self->{open_elements}} # <head>            pop @{$self->{open_elements}};
4709                    if $self->{insertion_mode} == AFTER_HEAD_IM;            pop @{$self->{open_elements}} # <head>
4710                !!!nack ('t101.1');                if $self->{insertion_mode} == AFTER_HEAD_IM;
4711                !!!next-token;            !!!nack ('t101.1');
4712                next B;            !!!next-token;
4713              } elsif ($token->{tag_name} eq 'link') {            next B;
4714                ## NOTE: There is a "as if in head" code clone.          } elsif ($token->{tag_name} eq 'link') {
4715                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            ## NOTE: There is a "as if in head" code clone.
4716                  !!!cp ('t102');            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4717                  !!!parse-error (type => 'after head',              !!!cp ('t102');
4718                                  text => $token->{tag_name}, token => $token);              !!!parse-error (type => 'after head',
4719                  push @{$self->{open_elements}},                              text => $token->{tag_name}, token => $token);
4720                      [$self->{head_element}, $el_category->{head}];              push @{$self->{open_elements}},
4721                } else {                  [$self->{head_element}, $el_category->{head}];
4722                  !!!cp ('t103');              $self->{head_element_inserted} = 1;
4723                }            } else {
4724                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);              !!!cp ('t103');
4725                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            }
4726                pop @{$self->{open_elements}} # <head>            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4727                    if $self->{insertion_mode} == AFTER_HEAD_IM;            pop @{$self->{open_elements}};
4728                !!!ack ('t103.1');            pop @{$self->{open_elements}} # <head>
4729                !!!next-token;                if $self->{insertion_mode} == AFTER_HEAD_IM;
4730                next B;            !!!ack ('t103.1');
4731              } elsif ($token->{tag_name} eq 'meta') {            !!!next-token;
4732                ## NOTE: There is a "as if in head" code clone.            next B;
4733                if ($self->{insertion_mode} == AFTER_HEAD_IM) {          } elsif ($token->{tag_name} eq 'command' or
4734                  !!!cp ('t104');                   $token->{tag_name} eq 'eventsource') {
4735                  !!!parse-error (type => 'after head',            if ($self->{insertion_mode} == IN_HEAD_IM) {
4736                                  text => $token->{tag_name}, token => $token);              ## NOTE: If the insertion mode at the time of the emission
4737                  push @{$self->{open_elements}},              ## of the token was "before head", $self->{insertion_mode}
4738                      [$self->{head_element}, $el_category->{head}];              ## is already changed to |IN_HEAD_IM|.
4739                } else {  
4740                  !!!cp ('t105');              ## NOTE: There is a "as if in head" code clone.
4741                }              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4742                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);              pop @{$self->{open_elements}};
4743                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.              pop @{$self->{open_elements}} # <head>
4744                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4745                !!!ack ('t103.2');
4746                !!!next-token;
4747                next B;
4748              } else {
4749                ## NOTE: "in head noscript" or "after head" insertion mode
4750                ## - in these cases, these tags are treated as same as
4751                ## normal in-body tags.
4752                !!!cp ('t103.3');
4753                #
4754              }
4755            } elsif ($token->{tag_name} eq 'meta') {
4756              ## NOTE: There is a "as if in head" code clone.
4757              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4758                !!!cp ('t104');
4759                !!!parse-error (type => 'after head',
4760                                text => $token->{tag_name}, token => $token);
4761                push @{$self->{open_elements}},
4762                    [$self->{head_element}, $el_category->{head}];
4763                $self->{head_element_inserted} = 1;
4764              } else {
4765                !!!cp ('t105');
4766              }
4767              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4768              my $meta_el = pop @{$self->{open_elements}};
4769    
4770                unless ($self->{confident}) {                unless ($self->{confident}) {
4771                  if ($token->{attributes}->{charset}) {                  if ($token->{attributes}->{charset}) {
# Line 4391  sub _tree_construction_main ($) { Line 4783  sub _tree_construction_main ($) {
4783                  } elsif ($token->{attributes}->{content}) {                  } elsif ($token->{attributes}->{content}) {
4784                    if ($token->{attributes}->{content}->{value}                    if ($token->{attributes}->{content}->{value}
4785                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4786                            [\x09-\x0D\x20]*=                            [\x09\x0A\x0C\x0D\x20]*=
4787                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4788                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20\x3B]*))/x) {                            ([^"'\x09\x0A\x0C\x0D\x20]
4789                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4790                      !!!cp ('t107');                      !!!cp ('t107');
4791                      ## NOTE: Whether the encoding is supported or not is handled                      ## NOTE: Whether the encoding is supported or not is handled
4792                      ## in the {change_encoding} callback.                      ## in the {change_encoding} callback.
# Line 4430  sub _tree_construction_main ($) { Line 4823  sub _tree_construction_main ($) {
4823                !!!ack ('t110.1');                !!!ack ('t110.1');
4824                !!!next-token;                !!!next-token;
4825                next B;                next B;
4826              } elsif ($token->{tag_name} eq 'title') {          } elsif ($token->{tag_name} eq 'title') {
4827                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4828                  !!!cp ('t111');              !!!cp ('t111');
4829                  ## As if </noscript>              ## As if </noscript>
4830                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4831                  !!!parse-error (type => 'in noscript', text => 'title',              !!!parse-error (type => 'in noscript', text => 'title',
4832                                  token => $token);                              token => $token);
4833                            
4834                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
4835                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
4836                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4837                  !!!cp ('t112');              !!!cp ('t112');
4838                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
4839                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
4840                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
4841                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
4842                } else {              $self->{head_element_inserted} = 1;
4843                  !!!cp ('t113');            } else {
4844                }              !!!cp ('t113');
4845              }
4846    
4847                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4848                my $parent = defined $self->{head_element} ? $self->{head_element}            $parse_rcdata->(RCDATA_CONTENT_MODEL);
4849                    : $self->{open_elements}->[-1]->[0];            ## ISSUE: A spec bug [Bug 6038]
4850                $parse_rcdata->(RCDATA_CONTENT_MODEL);            splice @{$self->{open_elements}}, -2, 1, () # <head>
4851                pop @{$self->{open_elements}} # <head>                if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4852                    if $self->{insertion_mode} == AFTER_HEAD_IM;            next B;
4853                next B;          } elsif ($token->{tag_name} eq 'style' or
4854              } elsif ($token->{tag_name} eq 'style' or                   $token->{tag_name} eq 'noframes') {
4855                       $token->{tag_name} eq 'noframes') {            ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4856                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and            ## insertion mode IN_HEAD_IM)
4857                ## insertion mode IN_HEAD_IM)            ## NOTE: There is a "as if in head" code clone.
4858                ## NOTE: There is a "as if in head" code clone.            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4859                if ($self->{insertion_mode} == AFTER_HEAD_IM) {              !!!cp ('t114');
4860                  !!!cp ('t114');              !!!parse-error (type => 'after head',
4861                  !!!parse-error (type => 'after head',                              text => $token->{tag_name}, token => $token);
4862                                  text => $token->{tag_name}, token => $token);              push @{$self->{open_elements}},
4863                  push @{$self->{open_elements}},                  [$self->{head_element}, $el_category->{head}];
4864                      [$self->{head_element}, $el_category->{head}];              $self->{head_element_inserted} = 1;
4865                } else {            } else {
4866                  !!!cp ('t115');              !!!cp ('t115');
4867                }            }
4868                $parse_rcdata->(CDATA_CONTENT_MODEL);            $parse_rcdata->(CDATA_CONTENT_MODEL);
4869                pop @{$self->{open_elements}} # <head>            ## ISSUE: A spec bug [Bug 6038]
4870                    if $self->{insertion_mode} == AFTER_HEAD_IM;            splice @{$self->{open_elements}}, -2, 1, () # <head>
4871                next B;                if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4872              } elsif ($token->{tag_name} eq 'noscript') {            next B;
4873            } elsif ($token->{tag_name} eq 'noscript') {
4874                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
4875                  !!!cp ('t116');                  !!!cp ('t116');
4876                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
# Line 4496  sub _tree_construction_main ($) { Line 4891  sub _tree_construction_main ($) {
4891                  !!!cp ('t118');                  !!!cp ('t118');
4892                  #                  #
4893                }                }
4894              } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
4895                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4896                  !!!cp ('t119');              !!!cp ('t119');
4897                  ## As if </noscript>              ## As if </noscript>
4898                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4899                  !!!parse-error (type => 'in noscript', text => 'script',              !!!parse-error (type => 'in noscript', text => 'script',
4900                                  token => $token);                              token => $token);
4901                            
4902                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
4903                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
4904                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4905                  !!!cp ('t120');              !!!cp ('t120');
4906                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
4907                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
4908                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
4909                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
4910                } else {              $self->{head_element_inserted} = 1;
4911                  !!!cp ('t121');            } else {
4912                }              !!!cp ('t121');
4913              }
4914    
4915                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4916                $script_start_tag->();            $script_start_tag->();
4917                pop @{$self->{open_elements}} # <head>            ## ISSUE: A spec bug  [Bug 6038]
4918                    if $self->{insertion_mode} == AFTER_HEAD_IM;            splice @{$self->{open_elements}}, -2, 1 # <head>
4919                next B;                if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4920              } elsif ($token->{tag_name} eq 'body' or            next B;
4921                       $token->{tag_name} eq 'frameset') {          } elsif ($token->{tag_name} eq 'body' or
4922                     $token->{tag_name} eq 'frameset') {
4923                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4924                  !!!cp ('t122');                  !!!cp ('t122');
4925                  ## As if </noscript>                  ## As if </noscript>
# Line 4657  sub _tree_construction_main ($) { Line 5054  sub _tree_construction_main ($) {
5054              } elsif ({              } elsif ({
5055                        body => 1, html => 1,                        body => 1, html => 1,
5056                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5057                if ($self->{insertion_mode} == BEFORE_HEAD_IM or                ## TODO: This branch is entirely redundant.
5058                  if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5059                    $self->{insertion_mode} == IN_HEAD_IM or                    $self->{insertion_mode} == IN_HEAD_IM or
5060                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5061                  !!!cp ('t140');                  !!!cp ('t140');
# Line 4829  sub _tree_construction_main ($) { Line 5227  sub _tree_construction_main ($) {
5227        } else {        } else {
5228          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5229        }        }
   
           ## ISSUE: An issue in the spec.  
5230      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
5231            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
5232              !!!cp ('t150');              !!!cp ('t150');
# Line 4850  sub _tree_construction_main ($) { Line 5246  sub _tree_construction_main ($) {
5246                  ## have an element in table scope                  ## have an element in table scope
5247                  for (reverse 0..$#{$self->{open_elements}}) {                  for (reverse 0..$#{$self->{open_elements}}) {
5248                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5249                    if ($node->[1] & TABLE_CELL_EL) {                    if ($node->[1] == TABLE_CELL_EL) {
5250                      !!!cp ('t151');                      !!!cp ('t151');
5251    
5252                      ## Close the cell                      ## Close the cell
# Line 4884  sub _tree_construction_main ($) { Line 5280  sub _tree_construction_main ($) {
5280                  INSCOPE: {                  INSCOPE: {
5281                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
5282                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
5283                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
5284                        !!!cp ('t155');                        !!!cp ('t155');
5285                        $i = $_;                        $i = $_;
5286                        last INSCOPE;                        last INSCOPE;
# Line 4910  sub _tree_construction_main ($) { Line 5306  sub _tree_construction_main ($) {
5306                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5307                  }                  }
5308    
5309                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
5310                    !!!cp ('t159');                    !!!cp ('t159');
5311                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
5312                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5007  sub _tree_construction_main ($) { Line 5403  sub _tree_construction_main ($) {
5403                  INSCOPE: {                  INSCOPE: {
5404                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
5405                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
5406                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
5407                        !!!cp ('t171');                        !!!cp ('t171');
5408                        $i = $_;                        $i = $_;
5409                        last INSCOPE;                        last INSCOPE;
# Line 5032  sub _tree_construction_main ($) { Line 5428  sub _tree_construction_main ($) {
5428                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5429                  }                  }
5430                                    
5431                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
5432                    !!!cp ('t175');                    !!!cp ('t175');
5433                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
5434                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5082  sub _tree_construction_main ($) { Line 5478  sub _tree_construction_main ($) {
5478                                line => $token->{line},                                line => $token->{line},
5479                                column => $token->{column}};                                column => $token->{column}};
5480                      next B;                      next B;
5481                    } elsif ($node->[1] & TABLE_CELL_EL) {                    } elsif ($node->[1] == TABLE_CELL_EL) {
5482                      !!!cp ('t180');                      !!!cp ('t180');
5483                      $tn = $node->[0]->manakai_local_name;                      $tn = $node->[0]->manakai_local_name;
5484                      ## NOTE: There is exactly one |td| or |th| element                      ## NOTE: There is exactly one |td| or |th| element
# Line 5111  sub _tree_construction_main ($) { Line 5507  sub _tree_construction_main ($) {
5507                my $i;                my $i;
5508                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5509                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5510                  if ($node->[1] & CAPTION_EL) {                  if ($node->[1] == CAPTION_EL) {
5511                    !!!cp ('t184');                    !!!cp ('t184');
5512                    $i = $_;                    $i = $_;
5513                    last INSCOPE;                    last INSCOPE;
# Line 5135  sub _tree_construction_main ($) { Line 5531  sub _tree_construction_main ($) {
5531                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5532                }                }
5533    
5534                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
5535                  !!!cp ('t188');                  !!!cp ('t188');
5536                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
5537                                  text => $self->{open_elements}->[-1]->[0]                                  text => $self->{open_elements}->[-1]->[0]
# Line 5202  sub _tree_construction_main ($) { Line 5598  sub _tree_construction_main ($) {
5598      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
5599        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
5600          if (not $open_tables->[-1]->[1] and # tainted          if (not $open_tables->[-1]->[1] and # tainted
5601              $token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5602            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5603                                
5604            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 5216  sub _tree_construction_main ($) { Line 5612  sub _tree_construction_main ($) {
5612    
5613          !!!parse-error (type => 'in table:#text', token => $token);          !!!parse-error (type => 'in table:#text', token => $token);
5614    
5615              ## As if in body, but insert into foster parent element          ## NOTE: As if in body, but insert into the foster parent element.
5616              ## ISSUE: Spec says that "whenever a node would be inserted          $reconstruct_active_formatting_elements->($insert_to_foster);
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
5617                            
5618              if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {          if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
5619                # MUST            # MUST
5620                my $foster_parent_element;            my $foster_parent_element;
5621                my $next_sibling;            my $next_sibling;
5622                my $prev_sibling;            my $prev_sibling;
5623                OE: for (reverse 0..$#{$self->{open_elements}}) {            OE: for (reverse 0..$#{$self->{open_elements}}) {
5624                  if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {              if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
5625                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5626                    if (defined $parent and $parent->node_type == 1) {                if (defined $parent and $parent->node_type == 1) {
5627                      !!!cp ('t196');                  $foster_parent_element = $parent;
5628                      $foster_parent_element = $parent;                  !!!cp ('t196');
5629                      $next_sibling = $self->{open_elements}->[$_]->[0];                  $next_sibling = $self->{open_elements}->[$_]->[0];
5630                      $prev_sibling = $next_sibling->previous_sibling;                  $prev_sibling = $next_sibling->previous_sibling;
5631                    } else {                  #
                     !!!cp ('t197');  
                     $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];  
                     $prev_sibling = $foster_parent_element->last_child;  
                   }  
                   last OE;  
                 }  
               } # OE  
               $foster_parent_element = $self->{open_elements}->[0]->[0] and  
               $prev_sibling = $foster_parent_element->last_child  
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 !!!cp ('t198');  
                 $prev_sibling->manakai_append_text ($token->{data});  
5632                } else {                } else {
5633                  !!!cp ('t199');                  !!!cp ('t197');
5634                  $foster_parent_element->insert_before                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5635                    ($self->{document}->create_text_node ($token->{data}),                  $prev_sibling = $foster_parent_element->last_child;
5636                     $next_sibling);                  #
5637                }                }
5638                  last OE;
5639                }
5640              } # OE
5641              $foster_parent_element = $self->{open_elements}->[0]->[0] and
5642              $prev_sibling = $foster_parent_element->last_child
5643                  unless defined $foster_parent_element;
5644              undef $prev_sibling unless $open_tables->[-1]->[2]; # ~node inserted
5645              if (defined $prev_sibling and
5646                  $prev_sibling->node_type == 3) {
5647                !!!cp ('t198');
5648                $prev_sibling->manakai_append_text ($token->{data});
5649              } else {
5650                !!!cp ('t199');
5651                $foster_parent_element->insert_before
5652                    ($self->{document}->create_text_node ($token->{data}),
5653                     $next_sibling);
5654              }
5655            $open_tables->[-1]->[1] = 1; # tainted            $open_tables->[-1]->[1] = 1; # tainted
5656              $open_tables->[-1]->[2] = 1; # ~node inserted
5657          } else {          } else {
5658              ## NOTE: Fragment case or in a foster parent'ed element
5659              ## (e.g. |<table><span>a|).  In fragment case, whether the
5660              ## character is appended to existing node or a new node is
5661              ## created is irrelevant, since the foster parent'ed nodes
5662              ## are discarded and fragment parsing does not invoke any
5663              ## script.
5664            !!!cp ('t200');            !!!cp ('t200');
5665            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});            $self->{open_elements}->[-1]->[0]->manakai_append_text
5666                  ($token->{data});
5667          }          }
5668                            
5669          !!!next-token;          !!!next-token;
# Line 5296  sub _tree_construction_main ($) { Line 5700  sub _tree_construction_main ($) {
5700                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5701              }              }
5702                                    
5703                  $self->{insertion_mode} = IN_ROW_IM;              $self->{insertion_mode} = IN_ROW_IM;
5704                  if ($token->{tag_name} eq 'tr') {              if ($token->{tag_name} eq 'tr') {
5705                    !!!cp ('t204');                !!!cp ('t204');
5706                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5707                    !!!nack ('t204');                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5708                    !!!next-token;                !!!nack ('t204');
5709                    next B;                !!!next-token;
5710                  } else {                next B;
5711                    !!!cp ('t205');              } else {
5712                    !!!insert-element ('tr',, $token);                !!!cp ('t205');
5713                    ## reprocess in the "in row" insertion mode                !!!insert-element ('tr',, $token);
5714                  }                ## reprocess in the "in row" insertion mode
5715                } else {              }
5716                  !!!cp ('t206');            } else {
5717                }              !!!cp ('t206');
5718              }
5719    
5720                ## Clear back to table row context                ## Clear back to table row context
5721                while (not ($self->{open_elements}->[-1]->[1]                while (not ($self->{open_elements}->[-1]->[1]
# Line 5319  sub _tree_construction_main ($) { Line 5724  sub _tree_construction_main ($) {
5724                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5725                }                }
5726                                
5727                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5728                $self->{insertion_mode} = IN_CELL_IM;            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5729              $self->{insertion_mode} = IN_CELL_IM;
5730    
5731                push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
5732                                
5733                !!!nack ('t207.1');            !!!nack ('t207.1');
5734              !!!next-token;
5735              next B;
5736            } elsif ({
5737                      caption => 1, col => 1, colgroup => 1,
5738                      tbody => 1, tfoot => 1, thead => 1,
5739                      tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5740                     }->{$token->{tag_name}}) {
5741              if ($self->{insertion_mode} == IN_ROW_IM) {
5742                ## As if </tr>
5743                ## have an element in table scope
5744                my $i;
5745                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5746                  my $node = $self->{open_elements}->[$_];
5747                  if ($node->[1] == TABLE_ROW_EL) {
5748                    !!!cp ('t208');
5749                    $i = $_;
5750                    last INSCOPE;
5751                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5752                    !!!cp ('t209');
5753                    last INSCOPE;
5754                  }
5755                } # INSCOPE
5756                unless (defined $i) {
5757                  !!!cp ('t210');
5758                  ## TODO: This type is wrong.
5759                  !!!parse-error (type => 'unmacthed end tag',
5760                                  text => $token->{tag_name}, token => $token);
5761                  ## Ignore the token
5762                  !!!nack ('t210.1');
5763                !!!next-token;                !!!next-token;
5764                next B;                next B;
5765              } elsif ({              }
                       caption => 1, col => 1, colgroup => 1,  
                       tbody => 1, tfoot => 1, thead => 1,  
                       tr => 1, # $self->{insertion_mode} == IN_ROW_IM  
                      }->{$token->{tag_name}}) {  
               if ($self->{insertion_mode} == IN_ROW_IM) {  
                 ## As if </tr>  
                 ## have an element in table scope  
                 my $i;  
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                   my $node = $self->{open_elements}->[$_];  
                   if ($node->[1] & TABLE_ROW_EL) {  
                     !!!cp ('t208');  
                     $i = $_;  
                     last INSCOPE;  
                   } elsif ($node->[1] & TABLE_SCOPING_EL) {  
                     !!!cp ('t209');  
                     last INSCOPE;  
                   }  
                 } # INSCOPE  
                 unless (defined $i) {  
                   !!!cp ('t210');  
 ## TODO: This type is wrong.  
                   !!!parse-error (type => 'unmacthed end tag',  
                                   text => $token->{tag_name}, token => $token);  
                   ## Ignore the token  
                   !!!nack ('t210.1');  
                   !!!next-token;  
                   next B;  
                 }  
5766                                    
5767                  ## Clear back to table row context                  ## Clear back to table row context
5768                  while (not ($self->{open_elements}->[-1]->[1]                  while (not ($self->{open_elements}->[-1]->[1]
# Line 5384  sub _tree_construction_main ($) { Line 5790  sub _tree_construction_main ($) {
5790                  my $i;                  my $i;
5791                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5792                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5793                    if ($node->[1] & TABLE_ROW_GROUP_EL) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
5794                      !!!cp ('t214');                      !!!cp ('t214');
5795                      $i = $_;                      $i = $_;
5796                      last INSCOPE;                      last INSCOPE;
# Line 5426  sub _tree_construction_main ($) { Line 5832  sub _tree_construction_main ($) {
5832                  !!!cp ('t218');                  !!!cp ('t218');
5833                }                }
5834    
5835                if ($token->{tag_name} eq 'col') {            if ($token->{tag_name} eq 'col') {
5836                  ## Clear back to table context              ## Clear back to table context
5837                  while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
5838                                  & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
5839                    !!!cp ('t219');                !!!cp ('t219');
5840                    ## ISSUE: Can this state be reached?                ## ISSUE: Can this state be reached?
5841                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5842                  }              }
5843                                
5844                  !!!insert-element ('colgroup',, $token);              !!!insert-element ('colgroup',, $token);
5845                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;              $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5846                  ## reprocess              ## reprocess
5847                  !!!ack-later;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5848                  next B;              !!!ack-later;
5849                } elsif ({              next B;
5850                          caption => 1,            } elsif ({
5851                          colgroup => 1,                      caption => 1,
5852                          tbody => 1, tfoot => 1, thead => 1,                      colgroup => 1,
5853                         }->{$token->{tag_name}}) {                      tbody => 1, tfoot => 1, thead => 1,
5854                  ## Clear back to table context                     }->{$token->{tag_name}}) {
5855                ## Clear back to table context
5856                  while (not ($self->{open_elements}->[-1]->[1]                  while (not ($self->{open_elements}->[-1]->[1]
5857                                  & TABLE_SCOPING_EL)) {                                  & TABLE_SCOPING_EL)) {
5858                    !!!cp ('t220');                    !!!cp ('t220');
# Line 5453  sub _tree_construction_main ($) { Line 5860  sub _tree_construction_main ($) {
5860                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5861                  }                  }
5862                                    
5863                  push @$active_formatting_elements, ['#marker', '']              push @$active_formatting_elements, ['#marker', '']
5864                      if $token->{tag_name} eq 'caption';                  if $token->{tag_name} eq 'caption';
5865                                    
5866                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5867                  $self->{insertion_mode} = {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5868                                             caption => IN_CAPTION_IM,              $self->{insertion_mode} = {
5869                                             colgroup => IN_COLUMN_GROUP_IM,                                         caption => IN_CAPTION_IM,
5870                                             tbody => IN_TABLE_BODY_IM,                                         colgroup => IN_COLUMN_GROUP_IM,
5871                                             tfoot => IN_TABLE_BODY_IM,                                         tbody => IN_TABLE_BODY_IM,
5872                                             thead => IN_TABLE_BODY_IM,                                         tfoot => IN_TABLE_BODY_IM,
5873                                            }->{$token->{tag_name}};                                         thead => IN_TABLE_BODY_IM,
5874                  !!!next-token;                                        }->{$token->{tag_name}};
5875                  !!!nack ('t220.1');              !!!next-token;
5876                  next B;              !!!nack ('t220.1');
5877                } else {              next B;
5878                  die "$0: in table: <>: $token->{tag_name}";            } else {
5879                }              die "$0: in table: <>: $token->{tag_name}";
5880              }
5881              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5882                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
5883                                text => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
# Line 5481  sub _tree_construction_main ($) { Line 5889  sub _tree_construction_main ($) {
5889                my $i;                my $i;
5890                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5891                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5892                  if ($node->[1] & TABLE_EL) {                  if ($node->[1] == TABLE_EL) {
5893                    !!!cp ('t221');                    !!!cp ('t221');
5894                    $i = $_;                    $i = $_;
5895                    last INSCOPE;                    last INSCOPE;
# Line 5508  sub _tree_construction_main ($) { Line 5916  sub _tree_construction_main ($) {
5916                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5917                }                }
5918    
5919                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {                unless ($self->{open_elements}->[-1]->[1] == TABLE_EL) {
5920                  !!!cp ('t225');                  !!!cp ('t225');
5921                  ## NOTE: |<table><tr><table>|                  ## NOTE: |<table><tr><table>|
5922                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
# Line 5532  sub _tree_construction_main ($) { Line 5940  sub _tree_construction_main ($) {
5940              !!!cp ('t227.8');              !!!cp ('t227.8');
5941              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
5942              $parse_rcdata->(CDATA_CONTENT_MODEL);              $parse_rcdata->(CDATA_CONTENT_MODEL);
5943                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5944              next B;              next B;
5945            } else {            } else {
5946              !!!cp ('t227.7');              !!!cp ('t227.7');
# Line 5542  sub _tree_construction_main ($) { Line 5951  sub _tree_construction_main ($) {
5951              !!!cp ('t227.6');              !!!cp ('t227.6');
5952              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
5953              $script_start_tag->();              $script_start_tag->();
5954                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5955              next B;              next B;
5956            } else {            } else {
5957              !!!cp ('t227.5');              !!!cp ('t227.5');
# Line 5557  sub _tree_construction_main ($) { Line 5967  sub _tree_construction_main ($) {
5967                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
5968    
5969                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5970                    $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5971    
5972                  ## TODO: form element pointer                  ## TODO: form element pointer
5973    
# Line 5594  sub _tree_construction_main ($) { Line 6005  sub _tree_construction_main ($) {
6005                my $i;                my $i;
6006                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6007                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6008                  if ($node->[1] & TABLE_ROW_EL) {                  if ($node->[1] == TABLE_ROW_EL) {
6009                    !!!cp ('t228');                    !!!cp ('t228');
6010                    $i = $_;                    $i = $_;
6011                    last INSCOPE;                    last INSCOPE;
# Line 5635  sub _tree_construction_main ($) { Line 6046  sub _tree_construction_main ($) {
6046                  my $i;                  my $i;
6047                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6048                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6049                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
6050                      !!!cp ('t233');                      !!!cp ('t233');
6051                      $i = $_;                      $i = $_;
6052                      last INSCOPE;                      last INSCOPE;
# Line 5673  sub _tree_construction_main ($) { Line 6084  sub _tree_construction_main ($) {
6084                  my $i;                  my $i;
6085                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6086                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6087                    if ($node->[1] & TABLE_ROW_GROUP_EL) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
6088                      !!!cp ('t237');                      !!!cp ('t237');
6089                      $i = $_;                      $i = $_;
6090                      last INSCOPE;                      last INSCOPE;
# Line 5720  sub _tree_construction_main ($) { Line 6131  sub _tree_construction_main ($) {
6131                my $i;                my $i;
6132                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6133                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6134                  if ($node->[1] & TABLE_EL) {                  if ($node->[1] == TABLE_EL) {
6135                    !!!cp ('t241');                    !!!cp ('t241');
6136                    $i = $_;                    $i = $_;
6137                    last INSCOPE;                    last INSCOPE;
# Line 5779  sub _tree_construction_main ($) { Line 6190  sub _tree_construction_main ($) {
6190                  my $i;                  my $i;
6191                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6192                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6193                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
6194                      !!!cp ('t250');                      !!!cp ('t250');
6195                      $i = $_;                      $i = $_;
6196                      last INSCOPE;                      last INSCOPE;
# Line 5869  sub _tree_construction_main ($) { Line 6280  sub _tree_construction_main ($) {
6280            #            #
6281          }          }
6282        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6283          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
6284                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
6285            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
6286            !!!cp ('t259.1');            !!!cp ('t259.1');
# Line 5886  sub _tree_construction_main ($) { Line 6297  sub _tree_construction_main ($) {
6297        }        }
6298      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6299            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6300              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6301                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6302                unless (length $token->{data}) {                unless (length $token->{data}) {
6303                  !!!cp ('t260');                  !!!cp ('t260');
# Line 5911  sub _tree_construction_main ($) { Line 6322  sub _tree_construction_main ($) {
6322              }              }
6323            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
6324              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
6325                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {                if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
6326                  !!!cp ('t264');                  !!!cp ('t264');
6327                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
6328                                  text => 'colgroup', token => $token);                                  text => 'colgroup', token => $token);
# Line 5937  sub _tree_construction_main ($) { Line 6348  sub _tree_construction_main ($) {
6348                #                #
6349              }              }
6350        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6351          if ($self->{open_elements}->[-1]->[1] & HTML_EL and          if ($self->{open_elements}->[-1]->[1] == HTML_EL and
6352              @{$self->{open_elements}} == 1) { # redundant, maybe              @{$self->{open_elements}} == 1) { # redundant, maybe
6353            !!!cp ('t270.2');            !!!cp ('t270.2');
6354            ## Stop parsing.            ## Stop parsing.
# Line 5955  sub _tree_construction_main ($) { Line 6366  sub _tree_construction_main ($) {
6366        }        }
6367    
6368            ## As if </colgroup>            ## As if </colgroup>
6369            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {            if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
6370              !!!cp ('t269');              !!!cp ('t269');
6371  ## TODO: Wrong error type?  ## TODO: Wrong error type?
6372              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 5980  sub _tree_construction_main ($) { Line 6391  sub _tree_construction_main ($) {
6391          next B;          next B;
6392        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6393          if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
6394            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
6395              !!!cp ('t272');              !!!cp ('t272');
6396              ## As if </option>              ## As if </option>
6397              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 5993  sub _tree_construction_main ($) { Line 6404  sub _tree_construction_main ($) {
6404            !!!next-token;            !!!next-token;
6405            next B;            next B;
6406          } elsif ($token->{tag_name} eq 'optgroup') {          } elsif ($token->{tag_name} eq 'optgroup') {
6407            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
6408              !!!cp ('t274');              !!!cp ('t274');
6409              ## As if </option>              ## As if </option>
6410              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6001  sub _tree_construction_main ($) { Line 6412  sub _tree_construction_main ($) {
6412              !!!cp ('t275');              !!!cp ('t275');
6413            }            }
6414    
6415            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
6416              !!!cp ('t276');              !!!cp ('t276');
6417              ## As if </optgroup>              ## As if </optgroup>
6418              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6031  sub _tree_construction_main ($) { Line 6442  sub _tree_construction_main ($) {
6442            my $i;            my $i;
6443            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6444              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6445              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
6446                !!!cp ('t278');                !!!cp ('t278');
6447                $i = $_;                $i = $_;
6448                last INSCOPE;                last INSCOPE;
# Line 6076  sub _tree_construction_main ($) { Line 6487  sub _tree_construction_main ($) {
6487          }          }
6488        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6489          if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
6490            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and            if ($self->{open_elements}->[-1]->[1] == OPTION_EL and
6491                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {                $self->{open_elements}->[-2]->[1] == OPTGROUP_EL) {
6492              !!!cp ('t283');              !!!cp ('t283');
6493              ## As if </option>              ## As if </option>
6494              splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
6495            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            } elsif ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
6496              !!!cp ('t284');              !!!cp ('t284');
6497              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6498            } else {            } else {
# Line 6094  sub _tree_construction_main ($) { Line 6505  sub _tree_construction_main ($) {
6505            !!!next-token;            !!!next-token;
6506            next B;            next B;
6507          } elsif ($token->{tag_name} eq 'option') {          } elsif ($token->{tag_name} eq 'option') {
6508            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
6509              !!!cp ('t286');              !!!cp ('t286');
6510              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6511            } else {            } else {
# Line 6111  sub _tree_construction_main ($) { Line 6522  sub _tree_construction_main ($) {
6522            my $i;            my $i;
6523            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6524              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6525              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
6526                !!!cp ('t288');                !!!cp ('t288');
6527                $i = $_;                $i = $_;
6528                last INSCOPE;                last INSCOPE;
# Line 6173  sub _tree_construction_main ($) { Line 6584  sub _tree_construction_main ($) {
6584            undef $i;            undef $i;
6585            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6586              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6587              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
6588                !!!cp ('t295');                !!!cp ('t295');
6589                $i = $_;                $i = $_;
6590                last INSCOPE;                last INSCOPE;
# Line 6212  sub _tree_construction_main ($) { Line 6623  sub _tree_construction_main ($) {
6623            next B;            next B;
6624          }          }
6625        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6626          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
6627                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
6628            !!!cp ('t299.1');            !!!cp ('t299.1');
6629            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6227  sub _tree_construction_main ($) { Line 6638  sub _tree_construction_main ($) {
6638        }        }
6639      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6640        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6641          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6642            my $data = $1;            my $data = $1;
6643            ## As if in body            ## As if in body
6644            $reconstruct_active_formatting_elements->($insert_to_current);            $reconstruct_active_formatting_elements->($insert_to_current);
# Line 6244  sub _tree_construction_main ($) { Line 6655  sub _tree_construction_main ($) {
6655          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6656            !!!cp ('t301');            !!!cp ('t301');
6657            !!!parse-error (type => 'after html:#text', token => $token);            !!!parse-error (type => 'after html:#text', token => $token);
6658              #
           ## Reprocess in the "after body" insertion mode.  
6659          } else {          } else {
6660            !!!cp ('t302');            !!!cp ('t302');
6661              ## "after body" insertion mode
6662              !!!parse-error (type => 'after body:#text', token => $token);
6663              #
6664          }          }
           
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:#text', token => $token);  
6665    
6666          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6667          ## reprocess          ## reprocess
# Line 6261  sub _tree_construction_main ($) { Line 6671  sub _tree_construction_main ($) {
6671            !!!cp ('t303');            !!!cp ('t303');
6672            !!!parse-error (type => 'after html',            !!!parse-error (type => 'after html',
6673                            text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
6674                        #
           ## Reprocess in the "after body" insertion mode.  
6675          } else {          } else {
6676            !!!cp ('t304');            !!!cp ('t304');
6677              ## "after body" insertion mode
6678              !!!parse-error (type => 'after body',
6679                              text => $token->{tag_name}, token => $token);
6680              #
6681          }          }
6682    
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body',  
                         text => $token->{tag_name}, token => $token);  
   
6683          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6684          !!!ack-later;          !!!ack-later;
6685          ## reprocess          ## reprocess
# Line 6281  sub _tree_construction_main ($) { Line 6690  sub _tree_construction_main ($) {
6690            !!!parse-error (type => 'after html:/',            !!!parse-error (type => 'after html:/',
6691                            text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
6692                        
6693            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6694            ## Reprocess in the "after body" insertion mode.            ## Reprocess.
6695              next B;
6696          } else {          } else {
6697            !!!cp ('t306');            !!!cp ('t306');
6698          }          }
# Line 6320  sub _tree_construction_main ($) { Line 6730  sub _tree_construction_main ($) {
6730        }        }
6731      } elsif ($self->{insertion_mode} & FRAME_IMS) {      } elsif ($self->{insertion_mode} & FRAME_IMS) {
6732        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6733          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6734            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6735                        
6736            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 6330  sub _tree_construction_main ($) { Line 6740  sub _tree_construction_main ($) {
6740            }            }
6741          }          }
6742                    
6743          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6744            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6745              !!!cp ('t311');              !!!cp ('t311');
6746              !!!parse-error (type => 'in frameset:#text', token => $token);              !!!parse-error (type => 'in frameset:#text', token => $token);
# Line 6400  sub _tree_construction_main ($) { Line 6810  sub _tree_construction_main ($) {
6810        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6811          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6812              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6813            if ($self->{open_elements}->[-1]->[1] & HTML_EL and            if ($self->{open_elements}->[-1]->[1] == HTML_EL and
6814                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
6815              !!!cp ('t325');              !!!cp ('t325');
6816              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 6414  sub _tree_construction_main ($) { Line 6824  sub _tree_construction_main ($) {
6824            }            }
6825    
6826            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
6827                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {                not ($self->{open_elements}->[-1]->[1] == FRAMESET_EL)) {
6828              !!!cp ('t327');              !!!cp ('t327');
6829              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
6830            } else {            } else {
# Line 6446  sub _tree_construction_main ($) { Line 6856  sub _tree_construction_main ($) {
6856            next B;            next B;
6857          }          }
6858        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6859          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
6860                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
6861            !!!cp ('t331.1');            !!!cp ('t331.1');
6862            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6459  sub _tree_construction_main ($) { Line 6869  sub _tree_construction_main ($) {
6869        } else {        } else {
6870          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6871        }        }
   
       ## ISSUE: An issue in spec here  
6872      } else {      } else {
6873        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
6874      }      }
# Line 6478  sub _tree_construction_main ($) { Line 6886  sub _tree_construction_main ($) {
6886          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
6887          next B;          next B;
6888        } elsif ({        } elsif ({
6889                  base => 1, link => 1,                  base => 1, command => 1, eventsource => 1, link => 1,
6890                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6891          !!!cp ('t334');          !!!cp ('t334');
6892          ## 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
6893          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6894          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}};
6895          !!!ack ('t334.1');          !!!ack ('t334.1');
6896          !!!next-token;          !!!next-token;
6897          next B;          next B;
6898        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
6899          ## 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
6900          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6901          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          my $meta_el = pop @{$self->{open_elements}};
6902    
6903          unless ($self->{confident}) {          unless ($self->{confident}) {
6904            if ($token->{attributes}->{charset}) {            if ($token->{attributes}->{charset}) {
# Line 6507  sub _tree_construction_main ($) { Line 6915  sub _tree_construction_main ($) {
6915            } elsif ($token->{attributes}->{content}) {            } elsif ($token->{attributes}->{content}) {
6916              if ($token->{attributes}->{content}->{value}              if ($token->{attributes}->{content}->{value}
6917                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6918                      [\x09-\x0D\x20]*=                      [\x09\x0A\x0C\x0D\x20]*=
6919                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                      [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6920                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20\x3B]*))/x) {                      ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6921                       /x) {
6922                !!!cp ('t336');                !!!cp ('t336');
6923                ## NOTE: Whether the encoding is supported or not is handled                ## NOTE: Whether the encoding is supported or not is handled
6924                ## in the {change_encoding} callback.                ## in the {change_encoding} callback.
# Line 6550  sub _tree_construction_main ($) { Line 6959  sub _tree_construction_main ($) {
6959          !!!parse-error (type => 'in body', text => 'body', token => $token);          !!!parse-error (type => 'in body', text => 'body', token => $token);
6960                                
6961          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
6962              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {              not ($self->{open_elements}->[1]->[1] == BODY_EL)) {
6963            !!!cp ('t342');            !!!cp ('t342');
6964            ## Ignore the token            ## Ignore the token
6965          } else {          } else {
# Line 6568  sub _tree_construction_main ($) { Line 6977  sub _tree_construction_main ($) {
6977          !!!next-token;          !!!next-token;
6978          next B;          next B;
6979        } elsif ({        } elsif ({
6980                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: Start tags for non-phrasing flow content elements
6981                  div => 1, dl => 1, fieldset => 1,  
6982                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  ## NOTE: The normal one
6983                  menu => 1, ol => 1, p => 1, ul => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
6984                    center => 1, datagrid => 1, details => 1, dialog => 1,
6985                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
6986                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
6987                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
6988                    section => 1, ul => 1,
6989                    ## NOTE: As normal, but drops leading newline
6990                  pre => 1, listing => 1,                  pre => 1, listing => 1,
6991                    ## NOTE: As normal, but interacts with the form element pointer
6992                  form => 1,                  form => 1,
6993                    
6994                  table => 1,                  table => 1,
6995                  hr => 1,                  hr => 1,
6996                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
# Line 6588  sub _tree_construction_main ($) { Line 7005  sub _tree_construction_main ($) {
7005    
7006          ## has a p element in scope          ## has a p element in scope
7007          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7008            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
7009              !!!cp ('t344');              !!!cp ('t344');
7010              !!!back-token; # <form>              !!!back-token; # <form>
7011              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
# Line 6640  sub _tree_construction_main ($) { Line 7057  sub _tree_construction_main ($) {
7057            !!!next-token;            !!!next-token;
7058          }          }
7059          next B;          next B;
7060        } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {        } elsif ($token->{tag_name} eq 'li') {
7061          ## has a p element in scope          ## NOTE: As normal, but imply </li> when there's another <li> ...
7062    
7063            ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
7064              ## Interpreted as <li><foo/></li><li/> (non-conforming)
7065              ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
7066              ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
7067              ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
7068              ## object (Fx)
7069              ## Generate non-tree (non-conforming)
7070              ## basefont (IE7 (where basefont is non-void)), center (IE),
7071              ## form (IE), hn (IE)
7072            ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7073              ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7074              ## div (Fx, S)
7075    
7076            my $non_optional;
7077            my $i = -1;
7078    
7079            ## 1.
7080            for my $node (reverse @{$self->{open_elements}}) {
7081              if ($node->[1] == LI_EL) {
7082                ## 2. (a) As if </li>
7083                {
7084                  ## If no </li> - not applied
7085                  #
7086    
7087                  ## Otherwise
7088    
7089                  ## 1. generate implied end tags, except for </li>
7090                  #
7091    
7092                  ## 2. If current node != "li", parse error
7093                  if ($non_optional) {
7094                    !!!parse-error (type => 'not closed',
7095                                    text => $non_optional->[0]->manakai_local_name,
7096                                    token => $token);
7097                    !!!cp ('t355');
7098                  } else {
7099                    !!!cp ('t356');
7100                  }
7101    
7102                  ## 3. Pop
7103                  splice @{$self->{open_elements}}, $i;
7104                }
7105    
7106                last; ## 2. (b) goto 5.
7107              } elsif (
7108                       ## NOTE: not "formatting" and not "phrasing"
7109                       ($node->[1] & SPECIAL_EL or
7110                        $node->[1] & SCOPING_EL) and
7111                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7112                       (not $node->[1] & ADDRESS_DIV_P_EL)
7113                      ) {
7114                ## 3.
7115                !!!cp ('t357');
7116                last; ## goto 5.
7117              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7118                !!!cp ('t358');
7119                #
7120              } else {
7121                !!!cp ('t359');
7122                $non_optional ||= $node;
7123                #
7124              }
7125              ## 4.
7126              ## goto 2.
7127              $i--;
7128            }
7129    
7130            ## 5. (a) has a |p| element in scope
7131          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7132            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
7133              !!!cp ('t353');              !!!cp ('t353');
7134    
7135                ## NOTE: |<p><li>|, for example.
7136    
7137              !!!back-token; # <x>              !!!back-token; # <x>
7138              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7139                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
# Line 6654  sub _tree_construction_main ($) { Line 7143  sub _tree_construction_main ($) {
7143              last INSCOPE;              last INSCOPE;
7144            }            }
7145          } # INSCOPE          } # INSCOPE
7146              
7147          ## Step 1          ## 5. (b) insert
7148            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7149            !!!nack ('t359.1');
7150            !!!next-token;
7151            next B;
7152          } elsif ($token->{tag_name} eq 'dt' or
7153                   $token->{tag_name} eq 'dd') {
7154            ## NOTE: As normal, but imply </dt> or </dd> when ...
7155    
7156            my $non_optional;
7157          my $i = -1;          my $i = -1;
7158          my $node = $self->{open_elements}->[$i];  
7159          my $li_or_dtdd = {li => {li => 1},          ## 1.
7160                            dt => {dt => 1, dd => 1},          for my $node (reverse @{$self->{open_elements}}) {
7161                            dd => {dt => 1, dd => 1}}->{$token->{tag_name}};            if ($node->[1] == DTDD_EL) {
7162          LI: {              ## 2. (a) As if </li>
7163            ## Step 2              {
7164            if ($li_or_dtdd->{$node->[0]->manakai_local_name}) {                ## If no </li> - not applied
7165              if ($i != -1) {                #
7166                !!!cp ('t355');  
7167                !!!parse-error (type => 'not closed',                ## Otherwise
7168                                text => $self->{open_elements}->[-1]->[0]  
7169                                    ->manakai_local_name,                ## 1. generate implied end tags, except for </dt> or </dd>
7170                                token => $token);                #
7171              } else {  
7172                !!!cp ('t356');                ## 2. If current node != "dt"|"dd", parse error
7173                  if ($non_optional) {
7174                    !!!parse-error (type => 'not closed',
7175                                    text => $non_optional->[0]->manakai_local_name,
7176                                    token => $token);
7177                    !!!cp ('t355.1');
7178                  } else {
7179                    !!!cp ('t356.1');
7180                  }
7181    
7182                  ## 3. Pop
7183                  splice @{$self->{open_elements}}, $i;
7184              }              }
7185              splice @{$self->{open_elements}}, $i;  
7186              last LI;              last; ## 2. (b) goto 5.
7187              } elsif (
7188                       ## NOTE: not "formatting" and not "phrasing"
7189                       ($node->[1] & SPECIAL_EL or
7190                        $node->[1] & SCOPING_EL) and
7191                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7192    
7193                       (not $node->[1] & ADDRESS_DIV_P_EL)
7194                      ) {
7195                ## 3.
7196                !!!cp ('t357.1');
7197                last; ## goto 5.
7198              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7199                !!!cp ('t358.1');
7200                #
7201            } else {            } else {
7202              !!!cp ('t357');              !!!cp ('t359.1');
7203            }              $non_optional ||= $node;
7204                          #
           ## 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;  
7205            }            }
7206                        ## 4.
7207            !!!cp ('t359');            ## goto 2.
           ## Step 4  
7208            $i--;            $i--;
7209            $node = $self->{open_elements}->[$i];          }
7210            redo LI;  
7211          } # LI          ## 5. (a) has a |p| element in scope
7212                      INSCOPE: for (reverse @{$self->{open_elements}}) {
7213              if ($_->[1] == P_EL) {
7214                !!!cp ('t353.1');
7215                !!!back-token; # <x>
7216                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7217                          line => $token->{line}, column => $token->{column}};
7218                next B;
7219              } elsif ($_->[1] & SCOPING_EL) {
7220                !!!cp ('t354.1');
7221                last INSCOPE;
7222              }
7223            } # INSCOPE
7224    
7225            ## 5. (b) insert
7226          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7227          !!!nack ('t359.1');          !!!nack ('t359.2');
7228          !!!next-token;          !!!next-token;
7229          next B;          next B;
7230        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
7231            ## NOTE: As normal, but effectively ends parsing
7232    
7233          ## has a p element in scope          ## has a p element in scope
7234          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7235            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
7236              !!!cp ('t367');              !!!cp ('t367');
7237              !!!back-token; # <plaintext>              !!!back-token; # <plaintext>
7238              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
# Line 6726  sub _tree_construction_main ($) { Line 7254  sub _tree_construction_main ($) {
7254        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
7255          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7256            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
7257            if ($node->[1] & A_EL) {            if ($node->[1] == A_EL) {
7258              !!!cp ('t371');              !!!cp ('t371');
7259              !!!parse-error (type => 'in a:a', token => $token);              !!!parse-error (type => 'in a:a', token => $token);
7260                            
# Line 6770  sub _tree_construction_main ($) { Line 7298  sub _tree_construction_main ($) {
7298          ## has a |nobr| element in scope          ## has a |nobr| element in scope
7299          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7300            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7301            if ($node->[1] & NOBR_EL) {            if ($node->[1] == NOBR_EL) {
7302              !!!cp ('t376');              !!!cp ('t376');
7303              !!!parse-error (type => 'in nobr:nobr', token => $token);              !!!parse-error (type => 'in nobr:nobr', token => $token);
7304              !!!back-token; # <nobr>              !!!back-token; # <nobr>
# Line 6793  sub _tree_construction_main ($) { Line 7321  sub _tree_construction_main ($) {
7321          ## has a button element in scope          ## has a button element in scope
7322          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7323            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7324            if ($node->[1] & BUTTON_EL) {            if ($node->[1] == BUTTON_EL) {
7325              !!!cp ('t378');              !!!cp ('t378');
7326              !!!parse-error (type => 'in button:button', token => $token);              !!!parse-error (type => 'in button:button', token => $token);
7327              !!!back-token; # <button>              !!!back-token; # <button>
# Line 6893  sub _tree_construction_main ($) { Line 7421  sub _tree_construction_main ($) {
7421            next B;            next B;
7422          }          }
7423        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
7424          my $tag_name = $token->{tag_name};          ## Step 1
7425          my $el;          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
         !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);  
7426                    
7427            ## Step 2
7428          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
7429    
7430            ## Step 3
7431            $self->{ignore_newline} = 1;
7432    
7433            ## Step 4
7434            ## ISSUE: This step is wrong. (r2302 enbugged)
7435    
7436            ## Step 5
7437          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
7438          delete $self->{escape}; # MUST          delete $self->{escape}; # MUST
7439            
7440          $insert->($el);          ## Step 6-7
7441                    $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
7442          my $text = '';  
7443          !!!nack ('t392.1');          !!!nack ('t392.1');
7444          !!!next-token;          !!!next-token;
7445          if ($token->{type} == CHARACTER_TOKEN) {          next B;
7446            $token->{data} =~ s/^\x0A//;        } elsif ($token->{tag_name} eq 'optgroup' or
7447            unless (length $token->{data}) {                 $token->{tag_name} eq 'option') {
7448              !!!cp ('t392');          ## has an |option| element in scope
7449              !!!next-token;          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7450            } else {            my $node = $self->{open_elements}->[$_];
7451              !!!cp ('t393');            if ($node->[1] == OPTION_EL) {
7452                !!!cp ('t397.1');
7453                ## NOTE: As if </option>
7454                !!!back-token; # <option> or <optgroup>
7455                $token = {type => END_TAG_TOKEN, tag_name => 'option',
7456                          line => $token->{line}, column => $token->{column}};
7457                next B;
7458              } elsif ($node->[1] & SCOPING_EL) {
7459                !!!cp ('t397.2');
7460                last INSCOPE;
7461            }            }
7462          } else {          } # INSCOPE
7463            !!!cp ('t394');  
7464          }          $reconstruct_active_formatting_elements->($insert_to_current);
7465          while ($token->{type} == CHARACTER_TOKEN) {  
7466            !!!cp ('t395');          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7467            $text .= $token->{data};  
7468            !!!next-token;          !!!nack ('t397.3');
         }  
         if (length $text) {  
           !!!cp ('t396');  
           $el->manakai_append_text ($text);  
         }  
           
         $self->{content_model} = PCDATA_CONTENT_MODEL;  
           
         if ($token->{type} == END_TAG_TOKEN and  
             $token->{tag_name} eq $tag_name) {  
           !!!cp ('t397');  
           ## Ignore the token  
         } else {  
           !!!cp ('t398');  
           !!!parse-error (type => 'in RCDATA:#eof', token => $token);  
         }  
7469          !!!next-token;          !!!next-token;
7470          next B;          redo B;
7471        } elsif ($token->{tag_name} eq 'rt' or        } elsif ($token->{tag_name} eq 'rt' or
7472                 $token->{tag_name} eq 'rp') {                 $token->{tag_name} eq 'rp') {
7473          ## has a |ruby| element in scope          ## has a |ruby| element in scope
7474          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7475            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7476            if ($node->[1] & RUBY_EL) {            if ($node->[1] == RUBY_EL) {
7477              !!!cp ('t398.1');              !!!cp ('t398.1');
7478              ## generate implied end tags              ## generate implied end tags
7479              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7480                !!!cp ('t398.2');                !!!cp ('t398.2');
7481                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
7482              }              }
7483              unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {              unless ($self->{open_elements}->[-1]->[1] == RUBY_EL) {
7484                !!!cp ('t398.3');                !!!cp ('t398.3');
7485                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
7486                                text => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
7487                                    ->manakai_local_name,                                    ->manakai_local_name,
7488                                token => $token);                                token => $token);
7489                pop @{$self->{open_elements}}                pop @{$self->{open_elements}}
7490                    while not $self->{open_elements}->[-1]->[1] & RUBY_EL;                    while not $self->{open_elements}->[-1]->[1] == RUBY_EL;
7491              }              }
7492              last INSCOPE;              last INSCOPE;
7493            } elsif ($node->[1] & SCOPING_EL) {            } elsif ($node->[1] & SCOPING_EL) {
# Line 6986  sub _tree_construction_main ($) { Line 7515  sub _tree_construction_main ($) {
7515                    
7516          if ($self->{self_closing}) {          if ($self->{self_closing}) {
7517            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
7518            !!!ack ('t398.1');            !!!ack ('t398.6');
7519          } else {          } else {
7520            !!!cp ('t398.2');            !!!cp ('t398.7');
7521            $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;            $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7522            ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion            ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7523            ## mode, "in body" (not "in foreign content") secondary insertion            ## mode, "in body" (not "in foreign content") secondary insertion
# Line 6999  sub _tree_construction_main ($) { Line 7528  sub _tree_construction_main ($) {
7528          next B;          next B;
7529        } elsif ({        } elsif ({
7530                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
7531                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1,
7532                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
7533                  thead => 1, tr => 1,                  thead => 1, tr => 1,
7534                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
# Line 7010  sub _tree_construction_main ($) { Line 7539  sub _tree_construction_main ($) {
7539          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7540          !!!next-token;          !!!next-token;
7541          next B;          next B;
7542                  } elsif ($token->{tag_name} eq 'param' or
7543          ## ISSUE: An issue on HTML5 new elements in the spec.                 $token->{tag_name} eq 'source') {
7544            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7545            pop @{$self->{open_elements}};
7546    
7547            !!!ack ('t398.5');
7548            !!!next-token;
7549            redo B;
7550        } else {        } else {
7551          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'image') {
7552            !!!cp ('t384');            !!!cp ('t384');
# Line 7034  sub _tree_construction_main ($) { Line 7569  sub _tree_construction_main ($) {
7569            !!!nack ('t380.1');            !!!nack ('t380.1');
7570          } elsif ({          } elsif ({
7571                    b => 1, big => 1, em => 1, font => 1, i => 1,                    b => 1, big => 1, em => 1, font => 1, i => 1,
7572                    s => 1, small => 1, strile => 1,                    s => 1, small => 1, strike => 1,
7573                    strong => 1, tt => 1, u => 1,                    strong => 1, tt => 1, u => 1,
7574                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
7575            !!!cp ('t375');            !!!cp ('t375');
# Line 7047  sub _tree_construction_main ($) { Line 7582  sub _tree_construction_main ($) {
7582            !!!ack ('t388.2');            !!!ack ('t388.2');
7583          } elsif ({          } elsif ({
7584                    area => 1, basefont => 1, bgsound => 1, br => 1,                    area => 1, basefont => 1, bgsound => 1, br => 1,
7585                    embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                    embed => 1, img => 1, spacer => 1, wbr => 1,
                   #image => 1,  
7586                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
7587            !!!cp ('t388.1');            !!!cp ('t388.1');
7588            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
# Line 7079  sub _tree_construction_main ($) { Line 7613  sub _tree_construction_main ($) {
7613          my $i;          my $i;
7614          INSCOPE: {          INSCOPE: {
7615            for (reverse @{$self->{open_elements}}) {            for (reverse @{$self->{open_elements}}) {
7616              if ($_->[1] & BODY_EL) {              if ($_->[1] == BODY_EL) {
7617                !!!cp ('t405');                !!!cp ('t405');
7618                $i = $_;                $i = $_;
7619                last INSCOPE;                last INSCOPE;
# Line 7089  sub _tree_construction_main ($) { Line 7623  sub _tree_construction_main ($) {
7623              }              }
7624            }            }
7625    
7626            !!!parse-error (type => 'start tag not allowed',            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
7627    
7628              !!!parse-error (type => 'unmatched end tag',
7629                            text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
7630            ## NOTE: Ignore the token.            ## NOTE: Ignore the token.
7631            !!!next-token;            !!!next-token;
# Line 7115  sub _tree_construction_main ($) { Line 7651  sub _tree_construction_main ($) {
7651          ## TODO: Update this code.  It seems that the code below is not          ## TODO: Update this code.  It seems that the code below is not
7652          ## up-to-date, though it has same effect as speced.          ## up-to-date, though it has same effect as speced.
7653          if (@{$self->{open_elements}} > 1 and          if (@{$self->{open_elements}} > 1 and
7654              $self->{open_elements}->[1]->[1] & BODY_EL) {              $self->{open_elements}->[1]->[1] == BODY_EL) {
7655            ## ISSUE: There is an issue in the spec.            unless ($self->{open_elements}->[-1]->[1] == BODY_EL) {
           unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {  
7656              !!!cp ('t406');              !!!cp ('t406');
7657              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
7658                              text => $self->{open_elements}->[1]->[0]                              text => $self->{open_elements}->[1]->[0]
# Line 7138  sub _tree_construction_main ($) { Line 7673  sub _tree_construction_main ($) {
7673            next B;            next B;
7674          }          }
7675        } elsif ({        } elsif ({
7676                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: End tags for non-phrasing flow content elements
7677                  div => 1, dl => 1, fieldset => 1, listing => 1,  
7678                  menu => 1, ol => 1, pre => 1, ul => 1,                  ## NOTE: The normal ones
7679                    address => 1, article => 1, aside => 1, blockquote => 1,
7680                    center => 1, datagrid => 1, details => 1, dialog => 1,
7681                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
7682                    footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
7683                    ol => 1, pre => 1, section => 1, ul => 1,
7684    
7685                    ## NOTE: As normal, but ... optional tags
7686                  dd => 1, dt => 1, li => 1,                  dd => 1, dt => 1, li => 1,
7687    
7688                  applet => 1, button => 1, marquee => 1, object => 1,                  applet => 1, button => 1, marquee => 1, object => 1,
7689                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7690            ## NOTE: Code for <li> start tags includes "as if </li>" code.
7691            ## Code for <dt> or <dd> start tags includes "as if </dt> or
7692            ## </dd>" code.
7693    
7694          ## has an element in scope          ## has an element in scope
7695          my $i;          my $i;
7696          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 7170  sub _tree_construction_main ($) { Line 7717  sub _tree_construction_main ($) {
7717                    dd => ($token->{tag_name} ne 'dd'),                    dd => ($token->{tag_name} ne 'dd'),
7718                    dt => ($token->{tag_name} ne 'dt'),                    dt => ($token->{tag_name} ne 'dt'),
7719                    li => ($token->{tag_name} ne 'li'),                    li => ($token->{tag_name} ne 'li'),
7720                      option => 1,
7721                      optgroup => 1,
7722                    p => 1,                    p => 1,
7723                    rt => 1,                    rt => 1,
7724                    rp => 1,                    rp => 1,
# Line 7202  sub _tree_construction_main ($) { Line 7751  sub _tree_construction_main ($) {
7751          !!!next-token;          !!!next-token;
7752          next B;          next B;
7753        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
7754            ## NOTE: As normal, but interacts with the form element pointer
7755    
7756          undef $self->{form_element};          undef $self->{form_element};
7757    
7758          ## has an element in scope          ## has an element in scope
7759          my $i;          my $i;
7760          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7761            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7762            if ($node->[1] & FORM_EL) {            if ($node->[1] == FORM_EL) {
7763              !!!cp ('t418');              !!!cp ('t418');
7764              $i = $_;              $i = $_;
7765              last INSCOPE;              last INSCOPE;
# Line 7249  sub _tree_construction_main ($) { Line 7800  sub _tree_construction_main ($) {
7800          !!!next-token;          !!!next-token;
7801          next B;          next B;
7802        } elsif ({        } elsif ({
7803                    ## NOTE: As normal, except acts as a closer for any ...
7804                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7805                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7806          ## has an element in scope          ## has an element in scope
7807          my $i;          my $i;
7808          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7809            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7810            if ($node->[1] & HEADING_EL) {            if ($node->[1] == HEADING_EL) {
7811              !!!cp ('t423');              !!!cp ('t423');
7812              $i = $_;              $i = $_;
7813              last INSCOPE;              last INSCOPE;
# Line 7294  sub _tree_construction_main ($) { Line 7846  sub _tree_construction_main ($) {
7846          !!!next-token;          !!!next-token;
7847          next B;          next B;
7848        } elsif ($token->{tag_name} eq 'p') {        } elsif ($token->{tag_name} eq 'p') {
7849            ## NOTE: As normal, except </p> implies <p> and ...
7850    
7851          ## has an element in scope          ## has an element in scope
7852            my $non_optional;
7853          my $i;          my $i;
7854          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7855            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7856            if ($node->[1] & P_EL) {            if ($node->[1] == P_EL) {
7857              !!!cp ('t410.1');              !!!cp ('t410.1');
7858              $i = $_;              $i = $_;
7859              last INSCOPE;              last INSCOPE;
7860            } elsif ($node->[1] & SCOPING_EL) {            } elsif ($node->[1] & SCOPING_EL) {
7861              !!!cp ('t411.1');              !!!cp ('t411.1');
7862              last INSCOPE;              last INSCOPE;
7863              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7864                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
7865                !!!cp ('t411.2');
7866                #
7867              } else {
7868                !!!cp ('t411.3');
7869                $non_optional ||= $node;
7870                #
7871            }            }
7872          } # INSCOPE          } # INSCOPE
7873    
7874          if (defined $i) {          if (defined $i) {
7875            if ($self->{open_elements}->[-1]->[0]->manakai_local_name            ## 1. Generate implied end tags
7876                    ne $token->{tag_name}) {            #
7877    
7878              ## 2. If current node != "p", parse error
7879              if ($non_optional) {
7880              !!!cp ('t412.1');              !!!cp ('t412.1');
7881              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
7882                              text => $self->{open_elements}->[-1]->[0]                              text => $non_optional->[0]->manakai_local_name,
                                 ->manakai_local_name,  
7883                              token => $token);                              token => $token);
7884            } else {            } else {
7885              !!!cp ('t414.1');              !!!cp ('t414.1');
7886            }            }
7887    
7888              ## 3. Pop
7889            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
7890          } else {          } else {
7891            !!!cp ('t413.1');            !!!cp ('t413.1');
# Line 7339  sub _tree_construction_main ($) { Line 7905  sub _tree_construction_main ($) {
7905        } elsif ({        } elsif ({
7906                  a => 1,                  a => 1,
7907                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
7908                  nobr => 1, s => 1, small => 1, strile => 1,                  nobr => 1, s => 1, small => 1, strike => 1,
7909                  strong => 1, tt => 1, u => 1,                  strong => 1, tt => 1, u => 1,
7910                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7911          !!!cp ('t427');          !!!cp ('t427');
# Line 7360  sub _tree_construction_main ($) { Line 7926  sub _tree_construction_main ($) {
7926          ## Ignore the token.          ## Ignore the token.
7927          !!!next-token;          !!!next-token;
7928          next B;          next B;
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                 area => 1, basefont => 1, bgsound => 1,  
                 embed => 1, hr => 1, iframe => 1, image => 1,  
                 img => 1, input => 1, isindex => 1, noembed => 1,  
                 noframes => 1, param => 1, select => 1, spacer => 1,  
                 table => 1, textarea => 1, wbr => 1,  
                 noscript => 0, ## TODO: if scripting is enabled  
                }->{$token->{tag_name}}) {  
         !!!cp ('t429');  
         !!!parse-error (type => 'unmatched end tag',  
                         text => $token->{tag_name}, token => $token);  
         ## Ignore the token  
         !!!next-token;  
         next B;  
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
7929        } else {        } else {
7930            if ($token->{tag_name} eq 'sarcasm') {
7931              sleep 0.001; # take a deep breath
7932            }
7933    
7934          ## Step 1          ## Step 1
7935          my $node_i = -1;          my $node_i = -1;
7936          my $node = $self->{open_elements}->[$node_i];          my $node = $self->{open_elements}->[$node_i];
7937    
7938          ## Step 2          ## Step 2
7939          S2: {          S2: {
7940            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {            my $node_tag_name = $node->[0]->manakai_local_name;
7941              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
7942              if ($node_tag_name eq $token->{tag_name}) {
7943              ## Step 1              ## Step 1
7944              ## generate implied end tags              ## generate implied end tags
7945              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
# Line 7401  sub _tree_construction_main ($) { Line 7952  sub _tree_construction_main ($) {
7952              }              }
7953                    
7954              ## Step 2              ## Step 2
7955              if ($self->{open_elements}->[-1]->[0]->manakai_local_name              my $current_tag_name
7956                      ne $token->{tag_name}) {                  = $self->{open_elements}->[-1]->[0]->manakai_local_name;
7957                $current_tag_name =~ tr/A-Z/a-z/;
7958                if ($current_tag_name ne $token->{tag_name}) {
7959                !!!cp ('t431');                !!!cp ('t431');
7960                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
7961                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
# Line 7430  sub _tree_construction_main ($) { Line 7983  sub _tree_construction_main ($) {
7983                ## Ignore the token                ## Ignore the token
7984                !!!next-token;                !!!next-token;
7985                last S2;                last S2;
             }  
7986    
7987                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7988                  ## 9.27, "a" is a child of <dd> (conforming).  In
7989                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7990                  ## "a" is a child of both <body> and <dd>.
7991                }
7992                
7993              !!!cp ('t434');              !!!cp ('t434');
7994            }            }
7995                        
# Line 7472  sub _tree_construction_main ($) { Line 8030  sub _tree_construction_main ($) {
8030    ## TODO: script stuffs    ## TODO: script stuffs
8031  } # _tree_construct_main  } # _tree_construct_main
8032    
8033  sub set_inner_html ($$$;$) {  sub set_inner_html ($$$$;$) {
8034    my $class = shift;    my $class = shift;
8035    my $node = shift;    my $node = shift;
8036    my $s = \$_[0];    #my $s = \$_[0];
8037    my $onerror = $_[1];    my $onerror = $_[1];
8038    my $get_wrapper = $_[2] || sub ($) { return $_[0] };    my $get_wrapper = $_[2] || sub ($) { return $_[0] };
8039    
# Line 7496  sub set_inner_html ($$$;$) { Line 8054  sub set_inner_html ($$$;$) {
8054      }      }
8055    
8056      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
8057      $class->parse_char_string ($$s => $node, $onerror, $get_wrapper);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
8058    } elsif ($nt == 1) {    } elsif ($nt == 1) {
8059      ## TODO: If non-html element      ## TODO: If non-html element
8060    
# Line 7515  sub set_inner_html ($$$;$) { Line 8073  sub set_inner_html ($$$;$) {
8073      my $i = 0;      my $i = 0;
8074      $p->{line_prev} = $p->{line} = 1;      $p->{line_prev} = $p->{line} = 1;
8075      $p->{column_prev} = $p->{column} = 0;      $p->{column_prev} = $p->{column} = 0;
8076      $p->{set_next_char} = sub {      require Whatpm::Charset::DecodeHandle;
8077        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
8078        $input = $get_wrapper->($input);
8079        $p->{set_nc} = sub {
8080        my $self = shift;        my $self = shift;
8081    
8082        pop @{$self->{prev_char}};        my $char = '';
8083        unshift @{$self->{prev_char}}, $self->{next_char};        if (defined $self->{next_nc}) {
8084            $char = $self->{next_nc};
8085        $self->{next_char} = -1 and return if $i >= length $$s;          delete $self->{next_nc};
8086        $self->{next_char} = ord substr $$s, $i++, 1;          $self->{nc} = ord $char;
8087          } else {
8088            $self->{char_buffer} = '';
8089            $self->{char_buffer_pos} = 0;
8090            
8091            my $count = $input->manakai_read_until
8092                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
8093                 $self->{char_buffer_pos});
8094            if ($count) {
8095              $self->{line_prev} = $self->{line};
8096              $self->{column_prev} = $self->{column};
8097              $self->{column}++;
8098              $self->{nc}
8099                  = ord substr ($self->{char_buffer},
8100                                $self->{char_buffer_pos}++, 1);
8101              return;
8102            }
8103            
8104            if ($input->read ($char, 1)) {
8105              $self->{nc} = ord $char;
8106            } else {
8107              $self->{nc} = -1;
8108              return;
8109            }
8110          }
8111    
8112        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8113        $p->{column}++;        $p->{column}++;
8114    
8115        if ($self->{next_char} == 0x000A) { # LF        if ($self->{nc} == 0x000A) { # LF
8116          $p->{line}++;          $p->{line}++;
8117          $p->{column} = 0;          $p->{column} = 0;
8118          !!!cp ('i1');          !!!cp ('i1');
8119        } elsif ($self->{next_char} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
8120          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
8121          $self->{next_char} = 0x000A; # LF # MUST          my $next = '';
8122            if ($input->read ($next, 1) and $next ne "\x0A") {
8123              $self->{next_nc} = $next;
8124            }
8125            $self->{nc} = 0x000A; # LF # MUST
8126          $p->{line}++;          $p->{line}++;
8127          $p->{column} = 0;          $p->{column} = 0;
8128          !!!cp ('i2');          !!!cp ('i2');
8129        } 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  
8130          !!!cp ('i4');          !!!cp ('i4');
8131          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
8132          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
       } elsif ($self->{next_char} <= 0x0008 or  
                (0x000E <= $self->{next_char} and  
                 $self->{next_char} <= 0x001F) or  
                (0x007F <= $self->{next_char} and  
                 $self->{next_char} <= 0x009F) or  
                (0xD800 <= $self->{next_char} and  
                 $self->{next_char} <= 0xDFFF) or  
                (0xFDD0 <= $self->{next_char} and  
                 $self->{next_char} <= 0xFDDF) or  
                {  
                 0xFFFE => 1, 0xFFFF => 1, 0x1FFFE => 1, 0x1FFFF => 1,  
                 0x2FFFE => 1, 0x2FFFF => 1, 0x3FFFE => 1, 0x3FFFF => 1,  
                 0x4FFFE => 1, 0x4FFFF => 1, 0x5FFFE => 1, 0x5FFFF => 1,  
                 0x6FFFE => 1, 0x6FFFF => 1, 0x7FFFE => 1, 0x7FFFF => 1,  
                 0x8FFFE => 1, 0x8FFFF => 1, 0x9FFFE => 1, 0x9FFFF => 1,  
                 0xAFFFE => 1, 0xAFFFF => 1, 0xBFFFE => 1, 0xBFFFF => 1,  
                 0xCFFFE => 1, 0xCFFFF => 1, 0xDFFFE => 1, 0xDFFFF => 1,  
                 0xEFFFE => 1, 0xEFFFF => 1, 0xFFFFE => 1, 0xFFFFF => 1,  
                 0x10FFFE => 1, 0x10FFFF => 1,  
                }->{$self->{next_char}}) {  
         !!!cp ('i4.1');  
         if ($self->{next_char} < 0x10000) {  
           !!!parse-error (type => 'control char',  
                           text => (sprintf 'U+%04X', $self->{next_char}));  
         } else {  
           !!!parse-error (type => 'control char',  
                           text => (sprintf 'U-%08X', $self->{next_char}));  
         }  
8133        }        }
8134      };      };
8135      $p->{prev_char} = [-1, -1, -1];  
8136      $p->{next_char} = -1;      $p->{read_until} = sub {
8137              #my ($scalar, $specials_range, $offset) = @_;
8138          return 0 if defined $p->{next_nc};
8139    
8140          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
8141          my $offset = $_[2] || 0;
8142          
8143          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
8144            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
8145            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
8146              substr ($_[0], $offset)
8147                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
8148              my $count = $+[0] - $-[0];
8149              if ($count) {
8150                $p->{column} += $count;
8151                $p->{char_buffer_pos} += $count;
8152                $p->{line_prev} = $p->{line};
8153                $p->{column_prev} = $p->{column} - 1;
8154                $p->{nc} = -1;
8155              }
8156              return $count;
8157            } else {
8158              return 0;
8159            }
8160          } else {
8161            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
8162            if ($count) {
8163              $p->{column} += $count;
8164              $p->{column_prev} += $count;
8165              $p->{nc} = -1;
8166            }
8167            return $count;
8168          }
8169        }; # $p->{read_until}
8170    
8171      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
8172        my (%opt) = @_;        my (%opt) = @_;
8173        my $line = $opt{line};        my $line = $opt{line};
# Line 7591  sub set_inner_html ($$$;$) { Line 8182  sub set_inner_html ($$$;$) {
8182        $ponerror->(line => $p->{line}, column => $p->{column}, @_);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
8183      };      };
8184            
8185        my $char_onerror = sub {
8186          my (undef, $type, %opt) = @_;
8187          $ponerror->(layer => 'encode',
8188                      line => $p->{line}, column => $p->{column} + 1,
8189                      %opt, type => $type);
8190        }; # $char_onerror
8191        $input->onerror ($char_onerror);
8192    
8193      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
8194      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
8195    
# Line 7626  sub set_inner_html ($$$;$) { Line 8225  sub set_inner_html ($$$;$) {
8225      push @{$p->{open_elements}}, [$root, $el_category->{html}];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
8226    
8227      undef $p->{head_element};      undef $p->{head_element};
8228        undef $p->{head_element_inserted};
8229    
8230      ## Step 6 # MUST      ## Step 6 # MUST
8231      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24