Parent Directory
|
Revision Log
|
Patch
| revision 1.152 by wakaba, Sun Jun 29 11:15:53 2008 UTC | revision 1.196 by wakaba, Sat Oct 4 07:58:58 2008 UTC | |
|---|---|---|
| # | Line 3 use strict; | Line 3 use strict; |
| 3 | our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r}; | our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r}; |
| 4 | use Error qw(:try); | use Error qw(:try); |
| 5 | ||
| 6 | ## NOTE: This module don't check all HTML5 parse errors; character | |
| 7 | ## encoding related parse errors are expected to be handled by relevant | |
| 8 | ## modules. | |
| 9 | ## Parse errors for control characters that are not allowed in HTML5 | |
| 10 | ## documents, for surrogate code points, and for noncharacter code | |
| 11 | ## points, as well as U+FFFD substitions for characters whose code points | |
| 12 | ## is higher than U+10FFFF may be detected by combining the parser with | |
| 13 | ## the checker implemented by Whatpm::Charset::UnicodeChecker (for its | |
| 14 | ## usage example, see |t/HTML-tree.t| in the Whatpm package or the | |
| 15 | ## WebHACC::Language::HTML module in the WebHACC package). | |
| 16 | ||
| 17 | ## ISSUE: | ## ISSUE: |
| 18 | ## var doc = implementation.createDocument (null, null, null); | ## var doc = implementation.createDocument (null, null, null); |
| 19 | ## doc.write (''); | ## doc.write (''); |
| # | Line 55 sub TABLE_ROWS_EL () { | Line 66 sub TABLE_ROWS_EL () { |
| 66 | } | } |
| 67 | ||
| 68 | ## NOTE: Used in "generate implied end tags" algorithm. | ## NOTE: Used in "generate implied end tags" algorithm. |
| 69 | ## NOTE: There is a code where a modified version of END_TAG_OPTIONAL_EL | ## NOTE: There is a code where a modified version of |
| 70 | ## is used in "generate implied end tags" implementation (search for the | ## END_TAG_OPTIONAL_EL is used in "generate implied end tags" |
| 71 | ## function mae). | ## implementation (search for the algorithm name). |
| 72 | sub END_TAG_OPTIONAL_EL () { | sub END_TAG_OPTIONAL_EL () { |
| 73 | DD_EL | | DD_EL | |
| 74 | DT_EL | | DT_EL | |
| 75 | LI_EL | | LI_EL | |
| 76 | OPTION_EL | | |
| 77 | OPTGROUP_EL | | |
| 78 | P_EL | | P_EL | |
| 79 | RUBY_COMPONENT_EL | RUBY_COMPONENT_EL |
| 80 | } | } |
| # | Line 130 my $el_category = { | Line 143 my $el_category = { |
| 143 | address => ADDRESS_EL, | address => ADDRESS_EL, |
| 144 | applet => MISC_SCOPING_EL, | applet => MISC_SCOPING_EL, |
| 145 | area => MISC_SPECIAL_EL, | area => MISC_SPECIAL_EL, |
| 146 | article => MISC_SPECIAL_EL, | |
| 147 | aside => MISC_SPECIAL_EL, | |
| 148 | b => FORMATTING_EL, | b => FORMATTING_EL, |
| 149 | base => MISC_SPECIAL_EL, | base => MISC_SPECIAL_EL, |
| 150 | basefont => MISC_SPECIAL_EL, | basefont => MISC_SPECIAL_EL, |
| # | Line 143 my $el_category = { | Line 158 my $el_category = { |
| 158 | center => MISC_SPECIAL_EL, | center => MISC_SPECIAL_EL, |
| 159 | col => MISC_SPECIAL_EL, | col => MISC_SPECIAL_EL, |
| 160 | colgroup => MISC_SPECIAL_EL, | colgroup => MISC_SPECIAL_EL, |
| 161 | command => MISC_SPECIAL_EL, | |
| 162 | datagrid => MISC_SPECIAL_EL, | |
| 163 | dd => DD_EL, | dd => DD_EL, |
| 164 | details => MISC_SPECIAL_EL, | |
| 165 | dialog => MISC_SPECIAL_EL, | |
| 166 | dir => MISC_SPECIAL_EL, | dir => MISC_SPECIAL_EL, |
| 167 | div => DIV_EL, | div => DIV_EL, |
| 168 | dl => MISC_SPECIAL_EL, | dl => MISC_SPECIAL_EL, |
| 169 | dt => DT_EL, | dt => DT_EL, |
| 170 | em => FORMATTING_EL, | em => FORMATTING_EL, |
| 171 | embed => MISC_SPECIAL_EL, | embed => MISC_SPECIAL_EL, |
| 172 | eventsource => MISC_SPECIAL_EL, | |
| 173 | fieldset => MISC_SPECIAL_EL, | fieldset => MISC_SPECIAL_EL, |
| 174 | figure => MISC_SPECIAL_EL, | |
| 175 | font => FORMATTING_EL, | font => FORMATTING_EL, |
| 176 | footer => MISC_SPECIAL_EL, | |
| 177 | form => FORM_EL, | form => FORM_EL, |
| 178 | frame => MISC_SPECIAL_EL, | frame => MISC_SPECIAL_EL, |
| 179 | frameset => FRAMESET_EL, | frameset => FRAMESET_EL, |
| # | Line 162 my $el_category = { | Line 184 my $el_category = { |
| 184 | h5 => HEADING_EL, | h5 => HEADING_EL, |
| 185 | h6 => HEADING_EL, | h6 => HEADING_EL, |
| 186 | head => MISC_SPECIAL_EL, | head => MISC_SPECIAL_EL, |
| 187 | header => MISC_SPECIAL_EL, | |
| 188 | hr => MISC_SPECIAL_EL, | hr => MISC_SPECIAL_EL, |
| 189 | html => HTML_EL, | html => HTML_EL, |
| 190 | i => FORMATTING_EL, | i => FORMATTING_EL, |
| 191 | iframe => MISC_SPECIAL_EL, | iframe => MISC_SPECIAL_EL, |
| 192 | img => MISC_SPECIAL_EL, | img => MISC_SPECIAL_EL, |
| 193 | #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec. | |
| 194 | input => MISC_SPECIAL_EL, | input => MISC_SPECIAL_EL, |
| 195 | isindex => MISC_SPECIAL_EL, | isindex => MISC_SPECIAL_EL, |
| 196 | li => LI_EL, | li => LI_EL, |
| # | Line 175 my $el_category = { | Line 199 my $el_category = { |
| 199 | marquee => MISC_SCOPING_EL, | marquee => MISC_SCOPING_EL, |
| 200 | menu => MISC_SPECIAL_EL, | menu => MISC_SPECIAL_EL, |
| 201 | meta => MISC_SPECIAL_EL, | meta => MISC_SPECIAL_EL, |
| 202 | nav => MISC_SPECIAL_EL, | |
| 203 | nobr => NOBR_EL | FORMATTING_EL, | nobr => NOBR_EL | FORMATTING_EL, |
| 204 | noembed => MISC_SPECIAL_EL, | noembed => MISC_SPECIAL_EL, |
| 205 | noframes => MISC_SPECIAL_EL, | noframes => MISC_SPECIAL_EL, |
| # | Line 193 my $el_category = { | Line 218 my $el_category = { |
| 218 | s => FORMATTING_EL, | s => FORMATTING_EL, |
| 219 | script => MISC_SPECIAL_EL, | script => MISC_SPECIAL_EL, |
| 220 | select => SELECT_EL, | select => SELECT_EL, |
| 221 | section => MISC_SPECIAL_EL, | |
| 222 | small => FORMATTING_EL, | small => FORMATTING_EL, |
| 223 | spacer => MISC_SPECIAL_EL, | spacer => MISC_SPECIAL_EL, |
| 224 | strike => FORMATTING_EL, | strike => FORMATTING_EL, |
| # | Line 312 my $foreign_attr_xname = { | Line 338 my $foreign_attr_xname = { |
| 338 | ||
| 339 | ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error. | ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error. |
| 340 | ||
| 341 | my $c1_entity_char = { | my $charref_map = { |
| 342 | 0x0D => 0x000A, | |
| 343 | 0x80 => 0x20AC, | 0x80 => 0x20AC, |
| 344 | 0x81 => 0xFFFD, | 0x81 => 0xFFFD, |
| 345 | 0x82 => 0x201A, | 0x82 => 0x201A, |
| # | Line 345 my $c1_entity_char = { | Line 372 my $c1_entity_char = { |
| 372 | 0x9D => 0xFFFD, | 0x9D => 0xFFFD, |
| 373 | 0x9E => 0x017E, | 0x9E => 0x017E, |
| 374 | 0x9F => 0x0178, | 0x9F => 0x0178, |
| 375 | }; # $c1_entity_char | }; # $charref_map |
| 376 | $charref_map->{$_} = 0xFFFD | |
| 377 | for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F, | |
| 378 | 0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF | |
| 379 | 0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF, | |
| 380 | 0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE, | |
| 381 | 0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF, | |
| 382 | 0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE, | |
| 383 | 0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF; | |
| 384 | ||
| 385 | ## TODO: Invoke the reset algorithm when a resettable element is | |
| 386 | ## created (cf. HTML5 revision 2259). | |
| 387 | ||
| 388 | sub parse_byte_string ($$$$;$) { | sub parse_byte_string ($$$$;$) { |
| 389 | my $self = shift; | my $self = shift; |
| # | Line 354 sub parse_byte_string ($$$$;$) { | Line 392 sub parse_byte_string ($$$$;$) { |
| 392 | return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]); | return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]); |
| 393 | } # parse_byte_string | } # parse_byte_string |
| 394 | ||
| 395 | sub parse_byte_stream ($$$$;$) { | sub parse_byte_stream ($$$$;$$) { |
| 396 | # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_; | |
| 397 | my $self = ref $_[0] ? shift : shift->new; | my $self = ref $_[0] ? shift : shift->new; |
| 398 | my $charset_name = shift; | my $charset_name = shift; |
| 399 | my $byte_stream = $_[0]; | my $byte_stream = $_[0]; |
| # | Line 365 sub parse_byte_stream ($$$$;$) { | Line 404 sub parse_byte_stream ($$$$;$) { |
| 404 | }; | }; |
| 405 | $self->{parse_error} = $onerror; # updated later by parse_char_string | $self->{parse_error} = $onerror; # updated later by parse_char_string |
| 406 | ||
| 407 | my $get_wrapper = $_[3] || sub ($) { | |
| 408 | return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle | |
| 409 | }; | |
| 410 | ||
| 411 | ## HTML5 encoding sniffing algorithm | ## HTML5 encoding sniffing algorithm |
| 412 | require Message::Charset::Info; | require Message::Charset::Info; |
| 413 | my $charset; | my $charset; |
| # | Line 372 sub parse_byte_stream ($$$$;$) { | Line 415 sub parse_byte_stream ($$$$;$) { |
| 415 | my ($char_stream, $e_status); | my ($char_stream, $e_status); |
| 416 | ||
| 417 | SNIFFING: { | SNIFFING: { |
| 418 | ## NOTE: By setting |allow_fallback| option true when the | |
| 419 | ## |get_decode_handle| method is invoked, we ignore what the HTML5 | |
| 420 | ## spec requires, i.e. unsupported encoding should be ignored. | |
| 421 | ## TODO: We should not do this unless the parser is invoked | |
| 422 | ## in the conformance checking mode, in which this behavior | |
| 423 | ## would be useful. | |
| 424 | ||
| 425 | ## Step 1 | ## Step 1 |
| 426 | if (defined $charset_name) { | if (defined $charset_name) { |
| 427 | $charset = Message::Charset::Info->get_by_iana_name ($charset_name); | $charset = Message::Charset::Info->get_by_html_name ($charset_name); |
| 428 | ## TODO: Is this ok? Transfer protocol's parameter should be | |
| 429 | ## interpreted in its semantics? | |
| 430 | ||
| ## ISSUE: Unsupported encoding is not ignored according to the spec. | ||
| 431 | ($char_stream, $e_status) = $charset->get_decode_handle | ($char_stream, $e_status) = $charset->get_decode_handle |
| 432 | ($byte_stream, allow_error_reporting => 1, | ($byte_stream, allow_error_reporting => 1, |
| 433 | allow_fallback => 1); | allow_fallback => 1); |
| # | Line 385 sub parse_byte_stream ($$$$;$) { | Line 435 sub parse_byte_stream ($$$$;$) { |
| 435 | $self->{confident} = 1; | $self->{confident} = 1; |
| 436 | last SNIFFING; | last SNIFFING; |
| 437 | } else { | } else { |
| 438 | ## TODO: unsupported error | !!!parse-error (type => 'charset:not supported', |
| 439 | layer => 'encode', | |
| 440 | line => 1, column => 1, | |
| 441 | value => $charset_name, | |
| 442 | level => $self->{level}->{uncertain}); | |
| 443 | } | } |
| 444 | } | } |
| 445 | ||
| # | Line 399 sub parse_byte_stream ($$$$;$) { | Line 453 sub parse_byte_stream ($$$$;$) { |
| 453 | ||
| 454 | ## Step 3 | ## Step 3 |
| 455 | if ($byte_buffer =~ /^\xFE\xFF/) { | if ($byte_buffer =~ /^\xFE\xFF/) { |
| 456 | $charset = Message::Charset::Info->get_by_iana_name ('utf-16be'); | $charset = Message::Charset::Info->get_by_html_name ('utf-16be'); |
| 457 | ($char_stream, $e_status) = $charset->get_decode_handle | ($char_stream, $e_status) = $charset->get_decode_handle |
| 458 | ($byte_stream, allow_error_reporting => 1, | ($byte_stream, allow_error_reporting => 1, |
| 459 | allow_fallback => 1, byte_buffer => \$byte_buffer); | allow_fallback => 1, byte_buffer => \$byte_buffer); |
| 460 | $self->{confident} = 1; | $self->{confident} = 1; |
| 461 | last SNIFFING; | last SNIFFING; |
| 462 | } elsif ($byte_buffer =~ /^\xFF\xFE/) { | } elsif ($byte_buffer =~ /^\xFF\xFE/) { |
| 463 | $charset = Message::Charset::Info->get_by_iana_name ('utf-16le'); | $charset = Message::Charset::Info->get_by_html_name ('utf-16le'); |
| 464 | ($char_stream, $e_status) = $charset->get_decode_handle | ($char_stream, $e_status) = $charset->get_decode_handle |
| 465 | ($byte_stream, allow_error_reporting => 1, | ($byte_stream, allow_error_reporting => 1, |
| 466 | allow_fallback => 1, byte_buffer => \$byte_buffer); | allow_fallback => 1, byte_buffer => \$byte_buffer); |
| 467 | $self->{confident} = 1; | $self->{confident} = 1; |
| 468 | last SNIFFING; | last SNIFFING; |
| 469 | } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) { | } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) { |
| 470 | $charset = Message::Charset::Info->get_by_iana_name ('utf-8'); | $charset = Message::Charset::Info->get_by_html_name ('utf-8'); |
| 471 | ($char_stream, $e_status) = $charset->get_decode_handle | ($char_stream, $e_status) = $charset->get_decode_handle |
| 472 | ($byte_stream, allow_error_reporting => 1, | ($byte_stream, allow_error_reporting => 1, |
| 473 | allow_fallback => 1, byte_buffer => \$byte_buffer); | allow_fallback => 1, byte_buffer => \$byte_buffer); |
| # | Line 432 sub parse_byte_stream ($$$$;$) { | Line 486 sub parse_byte_stream ($$$$;$) { |
| 486 | $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string | $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string |
| 487 | ($byte_buffer); | ($byte_buffer); |
| 488 | if (defined $charset_name) { | if (defined $charset_name) { |
| 489 | $charset = Message::Charset::Info->get_by_iana_name ($charset_name); | $charset = Message::Charset::Info->get_by_html_name ($charset_name); |
| 490 | ||
| 491 | ## ISSUE: Unsupported encoding is not ignored according to the spec. | ## ISSUE: Unsupported encoding is not ignored according to the spec. |
| 492 | require Whatpm::Charset::DecodeHandle; | require Whatpm::Charset::DecodeHandle; |
| # | Line 443 sub parse_byte_stream ($$$$;$) { | Line 497 sub parse_byte_stream ($$$$;$) { |
| 497 | allow_fallback => 1, byte_buffer => \$byte_buffer); | allow_fallback => 1, byte_buffer => \$byte_buffer); |
| 498 | if ($char_stream) { | if ($char_stream) { |
| 499 | $buffer->{buffer} = $byte_buffer; | $buffer->{buffer} = $byte_buffer; |
| 500 | !!!parse-error (type => 'sniffing:chardet', ## TODO: type name | !!!parse-error (type => 'sniffing:chardet', |
| 501 | value => $charset_name, | text => $charset_name, |
| 502 | level => $self->{info_level}, | level => $self->{level}->{info}, |
| 503 | layer => 'encode', | |
| 504 | line => 1, column => 1); | line => 1, column => 1); |
| 505 | $self->{confident} = 0; | $self->{confident} = 0; |
| 506 | last SNIFFING; | last SNIFFING; |
| # | Line 454 sub parse_byte_stream ($$$$;$) { | Line 509 sub parse_byte_stream ($$$$;$) { |
| 509 | ||
| 510 | ## Step 7: default | ## Step 7: default |
| 511 | ## TODO: Make this configurable. | ## TODO: Make this configurable. |
| 512 | $charset = Message::Charset::Info->get_by_iana_name ('windows-1252'); | $charset = Message::Charset::Info->get_by_html_name ('windows-1252'); |
| 513 | ## NOTE: We choose |windows-1252| here, since |utf-8| should be | ## NOTE: We choose |windows-1252| here, since |utf-8| should be |
| 514 | ## detectable in the step 6. | ## detectable in the step 6. |
| 515 | require Whatpm::Charset::DecodeHandle; | require Whatpm::Charset::DecodeHandle; |
| # | Line 466 sub parse_byte_stream ($$$$;$) { | Line 521 sub parse_byte_stream ($$$$;$) { |
| 521 | allow_fallback => 1, | allow_fallback => 1, |
| 522 | byte_buffer => \$byte_buffer); | byte_buffer => \$byte_buffer); |
| 523 | $buffer->{buffer} = $byte_buffer; | $buffer->{buffer} = $byte_buffer; |
| 524 | !!!parse-error (type => 'sniffing:default', ## TODO: type name | !!!parse-error (type => 'sniffing:default', |
| 525 | value => 'windows-1252', | text => 'windows-1252', |
| 526 | level => $self->{info_level}, | level => $self->{level}->{info}, |
| 527 | line => 1, column => 1); | line => 1, column => 1, |
| 528 | layer => 'encode'); | |
| 529 | $self->{confident} = 0; | $self->{confident} = 0; |
| 530 | } # SNIFFING | } # SNIFFING |
| 531 | ||
| $self->{input_encoding} = $charset->get_iana_name; | ||
| 532 | if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) { | if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) { |
| 533 | !!!parse-error (type => 'chardecode:fallback', ## TODO: type name | $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name? |
| 534 | value => $self->{input_encoding}, | !!!parse-error (type => 'chardecode:fallback', |
| 535 | level => $self->{unsupported_level}, | #text => $self->{input_encoding}, |
| 536 | line => 1, column => 1); | level => $self->{level}->{uncertain}, |
| 537 | line => 1, column => 1, | |
| 538 | layer => 'encode'); | |
| 539 | } elsif (not ($e_status & | } elsif (not ($e_status & |
| 540 | Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL())) { | Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) { |
| 541 | !!!parse-error (type => 'chardecode:no error', ## TODO: type name | $self->{input_encoding} = $charset->get_iana_name; |
| 542 | value => $self->{input_encoding}, | !!!parse-error (type => 'chardecode:no error', |
| 543 | level => $self->{unsupported_level}, | text => $self->{input_encoding}, |
| 544 | line => 1, column => 1); | level => $self->{level}->{uncertain}, |
| 545 | line => 1, column => 1, | |
| 546 | layer => 'encode'); | |
| 547 | } else { | |
| 548 | $self->{input_encoding} = $charset->get_iana_name; | |
| 549 | } | } |
| 550 | ||
| 551 | $self->{change_encoding} = sub { | $self->{change_encoding} = sub { |
| # | Line 492 sub parse_byte_stream ($$$$;$) { | Line 553 sub parse_byte_stream ($$$$;$) { |
| 553 | $charset_name = shift; | $charset_name = shift; |
| 554 | my $token = shift; | my $token = shift; |
| 555 | ||
| 556 | $charset = Message::Charset::Info->get_by_iana_name ($charset_name); | $charset = Message::Charset::Info->get_by_html_name ($charset_name); |
| 557 | ($char_stream, $e_status) = $charset->get_decode_handle | ($char_stream, $e_status) = $charset->get_decode_handle |
| 558 | ($byte_stream, allow_error_reporting => 1, allow_fallback => 1, | ($byte_stream, allow_error_reporting => 1, allow_fallback => 1, |
| 559 | byte_buffer => \ $buffer->{buffer}); | byte_buffer => \ $buffer->{buffer}); |
| # | Line 503 sub parse_byte_stream ($$$$;$) { | Line 564 sub parse_byte_stream ($$$$;$) { |
| 564 | ## Step 1 | ## Step 1 |
| 565 | if ($charset->{category} & | if ($charset->{category} & |
| 566 | Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) { | Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) { |
| 567 | $charset = Message::Charset::Info->get_by_iana_name ('utf-8'); | $charset = Message::Charset::Info->get_by_html_name ('utf-8'); |
| 568 | ($char_stream, $e_status) = $charset->get_decode_handle | ($char_stream, $e_status) = $charset->get_decode_handle |
| 569 | ($byte_stream, | ($byte_stream, |
| 570 | byte_buffer => \ $buffer->{buffer}); | byte_buffer => \ $buffer->{buffer}); |
| # | Line 513 sub parse_byte_stream ($$$$;$) { | Line 574 sub parse_byte_stream ($$$$;$) { |
| 574 | ## Step 2 | ## Step 2 |
| 575 | if (defined $self->{input_encoding} and | if (defined $self->{input_encoding} and |
| 576 | $self->{input_encoding} eq $charset_name) { | $self->{input_encoding} eq $charset_name) { |
| 577 | !!!parse-error (type => 'charset label:matching', ## TODO: type | !!!parse-error (type => 'charset label:matching', |
| 578 | value => $charset_name, | text => $charset_name, |
| 579 | level => $self->{info_level}); | level => $self->{level}->{info}); |
| 580 | $self->{confident} = 1; | $self->{confident} = 1; |
| 581 | return; | return; |
| 582 | } | } |
| 583 | ||
| 584 | !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}. | !!!parse-error (type => 'charset label detected', |
| 585 | ':'.$charset_name, level => 'w', token => $token); | text => $self->{input_encoding}, |
| 586 | value => $charset_name, | |
| 587 | level => $self->{level}->{warn}, | |
| 588 | token => $token); | |
| 589 | ||
| 590 | ## Step 3 | ## Step 3 |
| 591 | # if (can) { | # if (can) { |
| # | Line 537 sub parse_byte_stream ($$$$;$) { | Line 601 sub parse_byte_stream ($$$$;$) { |
| 601 | ||
| 602 | my $char_onerror = sub { | my $char_onerror = sub { |
| 603 | my (undef, $type, %opt) = @_; | my (undef, $type, %opt) = @_; |
| 604 | !!!parse-error (%opt, type => $type, | !!!parse-error (layer => 'encode', |
| 605 | line => $self->{line}, column => $self->{column} + 1); | line => $self->{line}, column => $self->{column} + 1, |
| 606 | %opt, type => $type); | |
| 607 | if ($opt{octets}) { | if ($opt{octets}) { |
| 608 | ${$opt{octets}} = "\x{FFFD}"; # relacement character | ${$opt{octets}} = "\x{FFFD}"; # relacement character |
| 609 | } | } |
| 610 | }; | }; |
| $char_stream->onerror ($char_onerror); | ||
| 611 | ||
| 612 | my @args = @_; shift @args; # $s | my $wrapped_char_stream = $get_wrapper->($char_stream); |
| 613 | $wrapped_char_stream->onerror ($char_onerror); | |
| 614 | ||
| 615 | my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef; | |
| 616 | my $return; | my $return; |
| 617 | try { | try { |
| 618 | $return = $self->parse_char_stream ($char_stream, @args); | $return = $self->parse_char_stream ($wrapped_char_stream, @args); |
| 619 | } catch Whatpm::HTML::RestartParser with { | } catch Whatpm::HTML::RestartParser with { |
| 620 | ## NOTE: Invoked after {change_encoding}. | ## NOTE: Invoked after {change_encoding}. |
| 621 | ||
| $self->{input_encoding} = $charset->get_iana_name; | ||
| 622 | if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) { | if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) { |
| 623 | !!!parse-error (type => 'chardecode:fallback', ## TODO: type name | $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name? |
| 624 | value => $self->{input_encoding}, | !!!parse-error (type => 'chardecode:fallback', |
| 625 | level => $self->{unsupported_level}, | level => $self->{level}->{uncertain}, |
| 626 | line => 1, column => 1); | #text => $self->{input_encoding}, |
| 627 | line => 1, column => 1, | |
| 628 | layer => 'encode'); | |
| 629 | } elsif (not ($e_status & | } elsif (not ($e_status & |
| 630 | Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL())) { | Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) { |
| 631 | !!!parse-error (type => 'chardecode:no error', ## TODO: type name | $self->{input_encoding} = $charset->get_iana_name; |
| 632 | value => $self->{input_encoding}, | !!!parse-error (type => 'chardecode:no error', |
| 633 | level => $self->{unsupported_level}, | text => $self->{input_encoding}, |
| 634 | line => 1, column => 1); | level => $self->{level}->{uncertain}, |
| 635 | line => 1, column => 1, | |
| 636 | layer => 'encode'); | |
| 637 | } else { | |
| 638 | $self->{input_encoding} = $charset->get_iana_name; | |
| 639 | } | } |
| 640 | $self->{confident} = 1; | $self->{confident} = 1; |
| 641 | $char_stream->onerror ($char_onerror); | |
| 642 | $return = $self->parse_char_stream ($char_stream, @args); | $wrapped_char_stream = $get_wrapper->($char_stream); |
| 643 | $wrapped_char_stream->onerror ($char_onerror); | |
| 644 | ||
| 645 | $return = $self->parse_char_stream ($wrapped_char_stream, @args); | |
| 646 | }; | }; |
| 647 | return $return; | return $return; |
| 648 | } # parse_byte_stream | } # parse_byte_stream |
| # | Line 581 sub parse_byte_stream ($$$$;$) { | Line 656 sub parse_byte_stream ($$$$;$) { |
| 656 | ## such as |parse_byte_string| in this module, must ensure that it does | ## such as |parse_byte_string| in this module, must ensure that it does |
| 657 | ## strip the BOM and never strip any ZWNBSP. | ## strip the BOM and never strip any ZWNBSP. |
| 658 | ||
| 659 | sub parse_char_string ($$$;$) { | sub parse_char_string ($$$;$$) { |
| 660 | #my ($self, $s, $doc, $onerror, $get_wrapper) = @_; | |
| 661 | my $self = shift; | my $self = shift; |
| require utf8; | ||
| 662 | my $s = ref $_[0] ? $_[0] : \($_[0]); | my $s = ref $_[0] ? $_[0] : \($_[0]); |
| 663 | open my $input, '<' . (utf8::is_utf8 ($$s) ? ':utf8' : ''), $s; | require Whatpm::Charset::DecodeHandle; |
| 664 | my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s); | |
| 665 | return $self->parse_char_stream ($input, @_[1..$#_]); | return $self->parse_char_stream ($input, @_[1..$#_]); |
| 666 | } # parse_char_string | } # parse_char_string |
| 667 | *parse_string = \&parse_char_string; | *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility. |
| 668 | ||
| 669 | sub parse_char_stream ($$$;$) { | sub parse_char_stream ($$$;$$) { |
| 670 | my $self = ref $_[0] ? shift : shift->new; | my $self = ref $_[0] ? shift : shift->new; |
| 671 | my $input = $_[0]; | my $input = $_[0]; |
| 672 | $self->{document} = $_[1]; | $self->{document} = $_[1]; |
| # | Line 601 sub parse_char_stream ($$$;$) { | Line 677 sub parse_char_stream ($$$;$) { |
| 677 | $self->{confident} = 1 unless exists $self->{confident}; | $self->{confident} = 1 unless exists $self->{confident}; |
| 678 | $self->{document}->input_encoding ($self->{input_encoding}) | $self->{document}->input_encoding ($self->{input_encoding}) |
| 679 | if defined $self->{input_encoding}; | if defined $self->{input_encoding}; |
| 680 | ## TODO: |{input_encoding}| is needless? | |
| 681 | ||
| my $i = 0; | ||
| 682 | $self->{line_prev} = $self->{line} = 1; | $self->{line_prev} = $self->{line} = 1; |
| 683 | $self->{column_prev} = $self->{column} = 0; | $self->{column_prev} = -1; |
| 684 | $self->{set_next_char} = sub { | $self->{column} = 0; |
| 685 | $self->{set_nc} = sub { | |
| 686 | my $self = shift; | my $self = shift; |
| 687 | ||
| 688 | pop @{$self->{prev_char}}; | my $char = ''; |
| 689 | unshift @{$self->{prev_char}}, $self->{next_char}; | if (defined $self->{next_nc}) { |
| 690 | $char = $self->{next_nc}; | |
| 691 | my $char; | delete $self->{next_nc}; |
| 692 | if (defined $self->{next_next_char}) { | $self->{nc} = ord $char; |
| $char = $self->{next_next_char}; | ||
| delete $self->{next_next_char}; | ||
| 693 | } else { | } else { |
| 694 | $char = $input->getc; | $self->{char_buffer} = ''; |
| 695 | $self->{char_buffer_pos} = 0; | |
| 696 | ||
| 697 | my $count = $input->manakai_read_until | |
| 698 | ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos}); | |
| 699 | if ($count) { | |
| 700 | $self->{line_prev} = $self->{line}; | |
| 701 | $self->{column_prev} = $self->{column}; | |
| 702 | $self->{column}++; | |
| 703 | $self->{nc} | |
| 704 | = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1); | |
| 705 | return; | |
| 706 | } | |
| 707 | ||
| 708 | if ($input->read ($char, 1)) { | |
| 709 | $self->{nc} = ord $char; | |
| 710 | } else { | |
| 711 | $self->{nc} = -1; | |
| 712 | return; | |
| 713 | } | |
| 714 | } | } |
| $self->{next_char} = -1 and return unless defined $char; | ||
| $self->{next_char} = ord $char; | ||
| 715 | ||
| 716 | ($self->{line_prev}, $self->{column_prev}) | ($self->{line_prev}, $self->{column_prev}) |
| 717 | = ($self->{line}, $self->{column}); | = ($self->{line}, $self->{column}); |
| 718 | $self->{column}++; | $self->{column}++; |
| 719 | ||
| 720 | if ($self->{next_char} == 0x000A) { # LF | if ($self->{nc} == 0x000A) { # LF |
| 721 | !!!cp ('j1'); | !!!cp ('j1'); |
| 722 | $self->{line}++; | $self->{line}++; |
| 723 | $self->{column} = 0; | $self->{column} = 0; |
| 724 | } elsif ($self->{next_char} == 0x000D) { # CR | } elsif ($self->{nc} == 0x000D) { # CR |
| 725 | !!!cp ('j2'); | !!!cp ('j2'); |
| 726 | my $next = $input->getc; | ## TODO: support for abort/streaming |
| 727 | if (defined $next and $next ne "\x0A") { | my $next = ''; |
| 728 | $self->{next_next_char} = $next; | if ($input->read ($next, 1) and $next ne "\x0A") { |
| 729 | $self->{next_nc} = $next; | |
| 730 | } | } |
| 731 | $self->{next_char} = 0x000A; # LF # MUST | $self->{nc} = 0x000A; # LF # MUST |
| 732 | $self->{line}++; | $self->{line}++; |
| 733 | $self->{column} = 0; | $self->{column} = 0; |
| 734 | } elsif ($self->{next_char} > 0x10FFFF) { | } elsif ($self->{nc} == 0x0000) { # NULL |
| !!!cp ('j3'); | ||
| $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST | ||
| } elsif ($self->{next_char} == 0x0000) { # NULL | ||
| 735 | !!!cp ('j4'); | !!!cp ('j4'); |
| 736 | !!!parse-error (type => 'NULL'); | !!!parse-error (type => 'NULL'); |
| 737 | $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST | $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST |
| } elsif ($self->{next_char} <= 0x0008 or | ||
| (0x000E <= $self->{next_char} and $self->{next_char} <= 0x001F) or | ||
| (0x007F <= $self->{next_char} and $self->{next_char} <= 0x009F) or | ||
| (0xD800 <= $self->{next_char} and $self->{next_char} <= 0xDFFF) or | ||
| (0xFDD0 <= $self->{next_char} and $self->{next_char} <= 0xFDDF) or | ||
| { | ||
| 0xFFFE => 1, 0xFFFF => 1, 0x1FFFE => 1, 0x1FFFF => 1, | ||
| 0x2FFFE => 1, 0x2FFFF => 1, 0x3FFFE => 1, 0x3FFFF => 1, | ||
| 0x4FFFE => 1, 0x4FFFF => 1, 0x5FFFE => 1, 0x5FFFF => 1, | ||
| 0x6FFFE => 1, 0x6FFFF => 1, 0x7FFFE => 1, 0x7FFFF => 1, | ||
| 0x8FFFE => 1, 0x8FFFF => 1, 0x9FFFE => 1, 0x9FFFF => 1, | ||
| 0xAFFFE => 1, 0xAFFFF => 1, 0xBFFFE => 1, 0xBFFFF => 1, | ||
| 0xCFFFE => 1, 0xCFFFF => 1, 0xDFFFE => 1, 0xDFFFF => 1, | ||
| 0xEFFFE => 1, 0xEFFFF => 1, 0xFFFFE => 1, 0xFFFFF => 1, | ||
| 0x10FFFE => 1, 0x10FFFF => 1, | ||
| }->{$self->{next_char}}) { | ||
| !!!cp ('j5'); | ||
| !!!parse-error (type => 'control char', level => $self->{must_level}); | ||
| ## TODO: error type documentation | ||
| 738 | } | } |
| 739 | }; | }; |
| 740 | $self->{prev_char} = [-1, -1, -1]; | |
| 741 | $self->{next_char} = -1; | $self->{read_until} = sub { |
| 742 | #my ($scalar, $specials_range, $offset) = @_; | |
| 743 | return 0 if defined $self->{next_nc}; | |
| 744 | ||
| 745 | my $pattern = qr/[^$_[1]\x00\x0A\x0D]/; | |
| 746 | my $offset = $_[2] || 0; | |
| 747 | ||
| 748 | if ($self->{char_buffer_pos} < length $self->{char_buffer}) { | |
| 749 | pos ($self->{char_buffer}) = $self->{char_buffer_pos}; | |
| 750 | if ($self->{char_buffer} =~ /\G(?>$pattern)+/) { | |
| 751 | substr ($_[0], $offset) | |
| 752 | = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]); | |
| 753 | my $count = $+[0] - $-[0]; | |
| 754 | if ($count) { | |
| 755 | $self->{column} += $count; | |
| 756 | $self->{char_buffer_pos} += $count; | |
| 757 | $self->{line_prev} = $self->{line}; | |
| 758 | $self->{column_prev} = $self->{column} - 1; | |
| 759 | $self->{nc} = -1; | |
| 760 | } | |
| 761 | return $count; | |
| 762 | } else { | |
| 763 | return 0; | |
| 764 | } | |
| 765 | } else { | |
| 766 | my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]); | |
| 767 | if ($count) { | |
| 768 | $self->{column} += $count; | |
| 769 | $self->{line_prev} = $self->{line}; | |
| 770 | $self->{column_prev} = $self->{column} - 1; | |
| 771 | $self->{nc} = -1; | |
| 772 | } | |
| 773 | return $count; | |
| 774 | } | |
| 775 | }; # $self->{read_until} | |
| 776 | ||
| 777 | my $onerror = $_[2] || sub { | my $onerror = $_[2] || sub { |
| 778 | my (%opt) = @_; | my (%opt) = @_; |
| # | Line 679 sub parse_char_stream ($$$;$) { | Line 784 sub parse_char_stream ($$$;$) { |
| 784 | $onerror->(line => $self->{line}, column => $self->{column}, @_); | $onerror->(line => $self->{line}, column => $self->{column}, @_); |
| 785 | }; | }; |
| 786 | ||
| 787 | my $char_onerror = sub { | |
| 788 | my (undef, $type, %opt) = @_; | |
| 789 | !!!parse-error (layer => 'encode', | |
| 790 | line => $self->{line}, column => $self->{column} + 1, | |
| 791 | %opt, type => $type); | |
| 792 | }; # $char_onerror | |
| 793 | ||
| 794 | if ($_[3]) { | |
| 795 | $input = $_[3]->($input); | |
| 796 | $input->onerror ($char_onerror); | |
| 797 | } else { | |
| 798 | $input->onerror ($char_onerror) unless defined $input->onerror; | |
| 799 | } | |
| 800 | ||
| 801 | $self->_initialize_tokenizer; | $self->_initialize_tokenizer; |
| 802 | $self->_initialize_tree_constructor; | $self->_initialize_tree_constructor; |
| 803 | $self->_construct_tree; | $self->_construct_tree; |
| # | Line 692 sub parse_char_stream ($$$;$) { | Line 811 sub parse_char_stream ($$$;$) { |
| 811 | sub new ($) { | sub new ($) { |
| 812 | my $class = shift; | my $class = shift; |
| 813 | my $self = bless { | my $self = bless { |
| 814 | must_level => 'm', | level => {must => 'm', |
| 815 | should_level => 's', | should => 's', |
| 816 | good_level => 'w', | warn => 'w', |
| 817 | warn_level => 'w', | info => 'i', |
| 818 | info_level => 'i', | uncertain => 'u'}, |
| unsupported_level => 'u', | ||
| 819 | }, $class; | }, $class; |
| 820 | $self->{set_next_char} = sub { | $self->{set_nc} = sub { |
| 821 | $self->{next_char} = -1; | $self->{nc} = -1; |
| 822 | }; | }; |
| 823 | $self->{parse_error} = sub { | $self->{parse_error} = sub { |
| 824 | # | # |
| # | Line 727 sub RCDATA_CONTENT_MODEL () { CM_ENTITY | Line 845 sub RCDATA_CONTENT_MODEL () { CM_ENTITY |
| 845 | sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP } | sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP } |
| 846 | ||
| 847 | sub DATA_STATE () { 0 } | sub DATA_STATE () { 0 } |
| 848 | sub ENTITY_DATA_STATE () { 1 } | #sub ENTITY_DATA_STATE () { 1 } |
| 849 | sub TAG_OPEN_STATE () { 2 } | sub TAG_OPEN_STATE () { 2 } |
| 850 | sub CLOSE_TAG_OPEN_STATE () { 3 } | sub CLOSE_TAG_OPEN_STATE () { 3 } |
| 851 | sub TAG_NAME_STATE () { 4 } | sub TAG_NAME_STATE () { 4 } |
| # | Line 738 sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 | Line 856 sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 |
| 856 | sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 } | sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 } |
| 857 | sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 } | sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 } |
| 858 | sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 } | sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 } |
| 859 | sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 } | #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 } |
| 860 | sub MARKUP_DECLARATION_OPEN_STATE () { 13 } | sub MARKUP_DECLARATION_OPEN_STATE () { 13 } |
| 861 | sub COMMENT_START_STATE () { 14 } | sub COMMENT_START_STATE () { 14 } |
| 862 | sub COMMENT_START_DASH_STATE () { 15 } | sub COMMENT_START_DASH_STATE () { 15 } |
| # | Line 761 sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STAT | Line 879 sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STAT |
| 879 | sub BOGUS_DOCTYPE_STATE () { 32 } | sub BOGUS_DOCTYPE_STATE () { 32 } |
| 880 | sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 } | sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 } |
| 881 | sub SELF_CLOSING_START_TAG_STATE () { 34 } | sub SELF_CLOSING_START_TAG_STATE () { 34 } |
| 882 | sub CDATA_BLOCK_STATE () { 35 } | sub CDATA_SECTION_STATE () { 35 } |
| 883 | sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec | |
| 884 | sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec | |
| 885 | sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec | |
| 886 | sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec | |
| 887 | sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec | |
| 888 | sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec | |
| 889 | sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec | |
| 890 | sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec | |
| 891 | ## NOTE: "Entity data state", "entity in attribute value state", and | |
| 892 | ## "consume a character reference" algorithm are jointly implemented | |
| 893 | ## using the following six states: | |
| 894 | sub ENTITY_STATE () { 44 } | |
| 895 | sub ENTITY_HASH_STATE () { 45 } | |
| 896 | sub NCR_NUM_STATE () { 46 } | |
| 897 | sub HEXREF_X_STATE () { 47 } | |
| 898 | sub HEXREF_HEX_STATE () { 48 } | |
| 899 | sub ENTITY_NAME_STATE () { 49 } | |
| 900 | sub PCDATA_STATE () { 50 } # "data state" in the spec | |
| 901 | ||
| 902 | sub DOCTYPE_TOKEN () { 1 } | sub DOCTYPE_TOKEN () { 1 } |
| 903 | sub COMMENT_TOKEN () { 2 } | sub COMMENT_TOKEN () { 2 } |
| # | Line 814 sub IN_COLUMN_GROUP_IM () { 0b10 } | Line 950 sub IN_COLUMN_GROUP_IM () { 0b10 } |
| 950 | sub _initialize_tokenizer ($) { | sub _initialize_tokenizer ($) { |
| 951 | my $self = shift; | my $self = shift; |
| 952 | $self->{state} = DATA_STATE; # MUST | $self->{state} = DATA_STATE; # MUST |
| 953 | #$self->{s_kwd}; # state keyword - initialized when used | |
| 954 | #$self->{entity__value}; # initialized when used | |
| 955 | #$self->{entity__match}; # initialized when used | |
| 956 | $self->{content_model} = PCDATA_CONTENT_MODEL; # be | $self->{content_model} = PCDATA_CONTENT_MODEL; # be |
| 957 | undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE | undef $self->{ct}; # current token |
| 958 | undef $self->{current_attribute}; | undef $self->{ca}; # current attribute |
| 959 | undef $self->{last_emitted_start_tag_name}; | undef $self->{last_stag_name}; # last emitted start tag name |
| 960 | undef $self->{last_attribute_value_state}; | #$self->{prev_state}; # initialized when used |
| 961 | delete $self->{self_closing}; | delete $self->{self_closing}; |
| 962 | $self->{char} = []; | $self->{char_buffer} = ''; |
| 963 | # $self->{next_char} | $self->{char_buffer_pos} = 0; |
| 964 | $self->{nc} = -1; # next input character | |
| 965 | #$self->{next_nc} | |
| 966 | !!!next-input-character; | !!!next-input-character; |
| 967 | $self->{token} = []; | $self->{token} = []; |
| 968 | # $self->{escape} | # $self->{escape} |
| # | Line 832 sub _initialize_tokenizer ($) { | Line 973 sub _initialize_tokenizer ($) { |
| 973 | ## CHARACTER_TOKEN, or END_OF_FILE_TOKEN | ## CHARACTER_TOKEN, or END_OF_FILE_TOKEN |
| 974 | ## ->{name} (DOCTYPE_TOKEN) | ## ->{name} (DOCTYPE_TOKEN) |
| 975 | ## ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN) | ## ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN) |
| 976 | ## ->{public_identifier} (DOCTYPE_TOKEN) | ## ->{pubid} (DOCTYPE_TOKEN) |
| 977 | ## ->{system_identifier} (DOCTYPE_TOKEN) | ## ->{sysid} (DOCTYPE_TOKEN) |
| 978 | ## ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag | ## ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag |
| 979 | ## ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN) | ## ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN) |
| 980 | ## ->{name} | ## ->{name} |
| # | Line 852 sub _initialize_tokenizer ($) { | Line 993 sub _initialize_tokenizer ($) { |
| 993 | ## has completed loading. If one has, then it MUST be executed | ## has completed loading. If one has, then it MUST be executed |
| 994 | ## and removed from the list. | ## and removed from the list. |
| 995 | ||
| 996 | ## NOTE: HTML5 "Writing HTML documents" section, applied to | ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) |
| 997 | ## documents and not to user agents and conformance checkers, | ## (This requirement was dropped from HTML5 spec, unfortunately.) |
| 998 | ## contains some requirements that are not detected by the | |
| 999 | ## parsing algorithm: | my $is_space = { |
| 1000 | ## - Some requirements on character encoding declarations. ## TODO | 0x0009 => 1, # CHARACTER TABULATION (HT) |
| 1001 | ## - "Elements MUST NOT contain content that their content model disallows." | 0x000A => 1, # LINE FEED (LF) |
| 1002 | ## ... Some are parse error, some are not (will be reported by c.c.). | #0x000B => 0, # LINE TABULATION (VT) |
| 1003 | ## - Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) ## TODO | 0x000C => 1, # FORM FEED (FF) |
| 1004 | ## - Text (in elements, attributes, and comments) SHOULD NOT contain | #0x000D => 1, # CARRIAGE RETURN (CR) |
| 1005 | ## control characters other than space characters. ## TODO: (what is control character? C0, C1 and DEL? Unicode control character?) | 0x0020 => 1, # SPACE (SP) |
| 1006 | }; | |
| ## TODO: HTML5 poses authors two SHOULD-level requirements that cannot | ||
| ## be detected by the HTML5 parsing algorithm: | ||
| ## - Text, | ||
| 1007 | ||
| 1008 | sub _get_next_token ($) { | sub _get_next_token ($) { |
| 1009 | my $self = shift; | my $self = shift; |
| 1010 | ||
| 1011 | if ($self->{self_closing}) { | if ($self->{self_closing}) { |
| 1012 | !!!parse-error (type => 'nestc', token => $self->{current_token}); | !!!parse-error (type => 'nestc', token => $self->{ct}); |
| 1013 | ## NOTE: The |self_closing| flag is only set by start tag token. | ## NOTE: The |self_closing| flag is only set by start tag token. |
| 1014 | ## In addition, when a start tag token is emitted, it is always set to | ## In addition, when a start tag token is emitted, it is always set to |
| 1015 | ## |current_token|. | ## |ct|. |
| 1016 | delete $self->{self_closing}; | delete $self->{self_closing}; |
| 1017 | } | } |
| 1018 | ||
| # | Line 884 sub _get_next_token ($) { | Line 1022 sub _get_next_token ($) { |
| 1022 | } | } |
| 1023 | ||
| 1024 | A: { | A: { |
| 1025 | if ($self->{state} == DATA_STATE) { | if ($self->{state} == PCDATA_STATE) { |
| 1026 | if ($self->{next_char} == 0x0026) { # & | ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model. |
| 1027 | ||
| 1028 | if ($self->{nc} == 0x0026) { # & | |
| 1029 | !!!cp (0.1); | |
| 1030 | ## NOTE: In the spec, the tokenizer is switched to the | |