| 372 |
my ($char_stream, $e_status); |
my ($char_stream, $e_status); |
| 373 |
|
|
| 374 |
SNIFFING: { |
SNIFFING: { |
| 375 |
|
## NOTE: By setting |allow_fallback| option true when the |
| 376 |
|
## |get_decode_handle| method is invoked, we ignore what the HTML5 |
| 377 |
|
## spec requires, i.e. unsupported encoding should be ignored. |
| 378 |
|
## TODO: We should not do this unless the parser is invoked |
| 379 |
|
## in the conformance checking mode, in which this behavior |
| 380 |
|
## would be useful. |
| 381 |
|
|
| 382 |
## Step 1 |
## Step 1 |
| 383 |
if (defined $charset_name) { |
if (defined $charset_name) { |
| 384 |
$charset = Message::Charset::Info->get_by_iana_name ($charset_name); |
$charset = Message::Charset::Info->get_by_html_name ($charset_name); |
| 385 |
|
## TODO: Is this ok? Transfer protocol's parameter should be |
| 386 |
|
## interpreted in its semantics? |
| 387 |
|
|
| 388 |
## ISSUE: Unsupported encoding is not ignored according to the spec. |
## ISSUE: Unsupported encoding is not ignored according to the spec. |
| 389 |
($char_stream, $e_status) = $charset->get_decode_handle |
($char_stream, $e_status) = $charset->get_decode_handle |
| 407 |
|
|
| 408 |
## Step 3 |
## Step 3 |
| 409 |
if ($byte_buffer =~ /^\xFE\xFF/) { |
if ($byte_buffer =~ /^\xFE\xFF/) { |
| 410 |
$charset = Message::Charset::Info->get_by_iana_name ('utf-16be'); |
$charset = Message::Charset::Info->get_by_html_name ('utf-16be'); |
| 411 |
($char_stream, $e_status) = $charset->get_decode_handle |
($char_stream, $e_status) = $charset->get_decode_handle |
| 412 |
($byte_stream, allow_error_reporting => 1, |
($byte_stream, allow_error_reporting => 1, |
| 413 |
allow_fallback => 1, byte_buffer => \$byte_buffer); |
allow_fallback => 1, byte_buffer => \$byte_buffer); |
| 414 |
$self->{confident} = 1; |
$self->{confident} = 1; |
| 415 |
last SNIFFING; |
last SNIFFING; |
| 416 |
} elsif ($byte_buffer =~ /^\xFF\xFE/) { |
} elsif ($byte_buffer =~ /^\xFF\xFE/) { |
| 417 |
$charset = Message::Charset::Info->get_by_iana_name ('utf-16le'); |
$charset = Message::Charset::Info->get_by_html_name ('utf-16le'); |
| 418 |
($char_stream, $e_status) = $charset->get_decode_handle |
($char_stream, $e_status) = $charset->get_decode_handle |
| 419 |
($byte_stream, allow_error_reporting => 1, |
($byte_stream, allow_error_reporting => 1, |
| 420 |
allow_fallback => 1, byte_buffer => \$byte_buffer); |
allow_fallback => 1, byte_buffer => \$byte_buffer); |
| 421 |
$self->{confident} = 1; |
$self->{confident} = 1; |
| 422 |
last SNIFFING; |
last SNIFFING; |
| 423 |
} elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) { |
} elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) { |
| 424 |
$charset = Message::Charset::Info->get_by_iana_name ('utf-8'); |
$charset = Message::Charset::Info->get_by_html_name ('utf-8'); |
| 425 |
($char_stream, $e_status) = $charset->get_decode_handle |
($char_stream, $e_status) = $charset->get_decode_handle |
| 426 |
($byte_stream, allow_error_reporting => 1, |
($byte_stream, allow_error_reporting => 1, |
| 427 |
allow_fallback => 1, byte_buffer => \$byte_buffer); |
allow_fallback => 1, byte_buffer => \$byte_buffer); |
| 440 |
$charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string |
$charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string |
| 441 |
($byte_buffer); |
($byte_buffer); |
| 442 |
if (defined $charset_name) { |
if (defined $charset_name) { |
| 443 |
$charset = Message::Charset::Info->get_by_iana_name ($charset_name); |
$charset = Message::Charset::Info->get_by_html_name ($charset_name); |
| 444 |
|
|
| 445 |
## ISSUE: Unsupported encoding is not ignored according to the spec. |
## ISSUE: Unsupported encoding is not ignored according to the spec. |
| 446 |
require Whatpm::Charset::DecodeHandle; |
require Whatpm::Charset::DecodeHandle; |
| 463 |
|
|
| 464 |
## Step 7: default |
## Step 7: default |
| 465 |
## TODO: Make this configurable. |
## TODO: Make this configurable. |
| 466 |
$charset = Message::Charset::Info->get_by_iana_name ('windows-1252'); |
$charset = Message::Charset::Info->get_by_html_name ('windows-1252'); |
| 467 |
## NOTE: We choose |windows-1252| here, since |utf-8| should be |
## NOTE: We choose |windows-1252| here, since |utf-8| should be |
| 468 |
## detectable in the step 6. |
## detectable in the step 6. |
| 469 |
require Whatpm::Charset::DecodeHandle; |
require Whatpm::Charset::DecodeHandle; |
| 483 |
$self->{confident} = 0; |
$self->{confident} = 0; |
| 484 |
} # SNIFFING |
} # SNIFFING |
| 485 |
|
|
|
$self->{input_encoding} = $charset->get_iana_name; |
|
| 486 |
if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) { |
if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) { |
| 487 |
|
$self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name? |
| 488 |
!!!parse-error (type => 'chardecode:fallback', |
!!!parse-error (type => 'chardecode:fallback', |
| 489 |
text => $self->{input_encoding}, |
#text => $self->{input_encoding}, |
| 490 |
level => $self->{level}->{uncertain}, |
level => $self->{level}->{uncertain}, |
| 491 |
line => 1, column => 1, |
line => 1, column => 1, |
| 492 |
layer => 'encode'); |
layer => 'encode'); |
| 493 |
} elsif (not ($e_status & |
} elsif (not ($e_status & |
| 494 |
Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL())) { |
Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL())) { |
| 495 |
|
$self->{input_encoding} = $charset->get_iana_name; |
| 496 |
!!!parse-error (type => 'chardecode:no error', |
!!!parse-error (type => 'chardecode:no error', |
| 497 |
text => $self->{input_encoding}, |
text => $self->{input_encoding}, |
| 498 |
level => $self->{level}->{uncertain}, |
level => $self->{level}->{uncertain}, |
| 499 |
line => 1, column => 1, |
line => 1, column => 1, |
| 500 |
layer => 'encode'); |
layer => 'encode'); |
| 501 |
|
} else { |
| 502 |
|
$self->{input_encoding} = $charset->get_iana_name; |
| 503 |
} |
} |
| 504 |
|
|
| 505 |
$self->{change_encoding} = sub { |
$self->{change_encoding} = sub { |
| 507 |
$charset_name = shift; |
$charset_name = shift; |
| 508 |
my $token = shift; |
my $token = shift; |
| 509 |
|
|
| 510 |
$charset = Message::Charset::Info->get_by_iana_name ($charset_name); |
$charset = Message::Charset::Info->get_by_html_name ($charset_name); |
| 511 |
($char_stream, $e_status) = $charset->get_decode_handle |
($char_stream, $e_status) = $charset->get_decode_handle |
| 512 |
($byte_stream, allow_error_reporting => 1, allow_fallback => 1, |
($byte_stream, allow_error_reporting => 1, allow_fallback => 1, |
| 513 |
byte_buffer => \ $buffer->{buffer}); |
byte_buffer => \ $buffer->{buffer}); |
| 518 |
## Step 1 |
## Step 1 |
| 519 |
if ($charset->{category} & |
if ($charset->{category} & |
| 520 |
Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) { |
Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) { |
| 521 |
$charset = Message::Charset::Info->get_by_iana_name ('utf-8'); |
$charset = Message::Charset::Info->get_by_html_name ('utf-8'); |
| 522 |
($char_stream, $e_status) = $charset->get_decode_handle |
($char_stream, $e_status) = $charset->get_decode_handle |
| 523 |
($byte_stream, |
($byte_stream, |
| 524 |
byte_buffer => \ $buffer->{buffer}); |
byte_buffer => \ $buffer->{buffer}); |
| 571 |
} catch Whatpm::HTML::RestartParser with { |
} catch Whatpm::HTML::RestartParser with { |
| 572 |
## NOTE: Invoked after {change_encoding}. |
## NOTE: Invoked after {change_encoding}. |
| 573 |
|
|
|
$self->{input_encoding} = $charset->get_iana_name; |
|
| 574 |
if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) { |
if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) { |
| 575 |
|
$self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name? |
| 576 |
!!!parse-error (type => 'chardecode:fallback', |
!!!parse-error (type => 'chardecode:fallback', |
|
text => $self->{input_encoding}, |
|
| 577 |
level => $self->{level}->{uncertain}, |
level => $self->{level}->{uncertain}, |
| 578 |
|
#text => $self->{input_encoding}, |
| 579 |
line => 1, column => 1, |
line => 1, column => 1, |
| 580 |
layer => 'encode'); |
layer => 'encode'); |
| 581 |
} elsif (not ($e_status & |
} elsif (not ($e_status & |
| 582 |
Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL())) { |
Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL())) { |
| 583 |
|
$self->{input_encoding} = $charset->get_iana_name; |
| 584 |
!!!parse-error (type => 'chardecode:no error', |
!!!parse-error (type => 'chardecode:no error', |
| 585 |
text => $self->{input_encoding}, |
text => $self->{input_encoding}, |
| 586 |
level => $self->{level}->{uncertain}, |
level => $self->{level}->{uncertain}, |
| 587 |
line => 1, column => 1, |
line => 1, column => 1, |
| 588 |
layer => 'encode'); |
layer => 'encode'); |
| 589 |
|
} else { |
| 590 |
|
$self->{input_encoding} = $charset->get_iana_name; |
| 591 |
} |
} |
| 592 |
$self->{confident} = 1; |
$self->{confident} = 1; |
| 593 |
$char_stream->onerror ($char_onerror); |
$char_stream->onerror ($char_onerror); |
| 722 |
my $class = shift; |
my $class = shift; |
| 723 |
my $self = bless { |
my $self = bless { |
| 724 |
level => {must => 'm', |
level => {must => 'm', |
| 725 |
|
should => 's', |
| 726 |
warn => 'w', |
warn => 'w', |
| 727 |
info => 'i', |
info => 'i', |
| 728 |
uncertain => 'u'}, |
uncertain => 'u'}, |
| 1555 |
|
|
| 1556 |
redo A; |
redo A; |
| 1557 |
} else { |
} else { |
| 1558 |
!!!cp (82); |
if ($self->{next_char} == 0x0022 or # " |
| 1559 |
|
$self->{next_char} == 0x0027) { # ' |
| 1560 |
|
!!!cp (78); |
| 1561 |
|
!!!parse-error (type => 'bad attribute name'); |
| 1562 |
|
} else { |
| 1563 |
|
!!!cp (82); |
| 1564 |
|
} |
| 1565 |
$self->{current_attribute} |
$self->{current_attribute} |
| 1566 |
= {name => chr ($self->{next_char}), |
= {name => chr ($self->{next_char}), |
| 1567 |
value => '', |
value => '', |
| 1596 |
!!!next-input-character; |
!!!next-input-character; |
| 1597 |
redo A; |
redo A; |
| 1598 |
} elsif ($self->{next_char} == 0x003E) { # > |
} elsif ($self->{next_char} == 0x003E) { # > |
| 1599 |
|
!!!parse-error (type => 'empty unquoted attribute value'); |
| 1600 |
if ($self->{current_token}->{type} == START_TAG_TOKEN) { |
if ($self->{current_token}->{type} == START_TAG_TOKEN) { |
| 1601 |
!!!cp (87); |
!!!cp (87); |
| 1602 |
$self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; |
$self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name}; |
| 3169 |
## language. |
## language. |
| 3170 |
my $doctype_name = $token->{name}; |
my $doctype_name = $token->{name}; |
| 3171 |
$doctype_name = '' unless defined $doctype_name; |
$doctype_name = '' unless defined $doctype_name; |
| 3172 |
$doctype_name =~ tr/a-z/A-Z/; |
$doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive |
| 3173 |
if (not defined $token->{name} or # <!DOCTYPE> |
if (not defined $token->{name} or # <!DOCTYPE> |
|
defined $token->{public_identifier} or |
|
| 3174 |
defined $token->{system_identifier}) { |
defined $token->{system_identifier}) { |
| 3175 |
!!!cp ('t1'); |
!!!cp ('t1'); |
| 3176 |
!!!parse-error (type => 'not HTML5', token => $token); |
!!!parse-error (type => 'not HTML5', token => $token); |
| 3177 |
} elsif ($doctype_name ne 'HTML') { |
} elsif ($doctype_name ne 'HTML') { |
| 3178 |
!!!cp ('t2'); |
!!!cp ('t2'); |
|
## ISSUE: ASCII case-insensitive? (in fact it does not matter) |
|
| 3179 |
!!!parse-error (type => 'not HTML5', token => $token); |
!!!parse-error (type => 'not HTML5', token => $token); |
| 3180 |
|
} elsif (defined $token->{public_identifier}) { |
| 3181 |
|
if ($token->{public_identifier} eq 'XSLT-compat') { |
| 3182 |
|
!!!cp ('t1.2'); |
| 3183 |
|
!!!parse-error (type => 'XSLT-compat', token => $token, |
| 3184 |
|
level => $self->{level}->{should}); |
| 3185 |
|
} else { |
| 3186 |
|
!!!parse-error (type => 'not HTML5', token => $token); |
| 3187 |
|
} |
| 3188 |
} else { |
} else { |
| 3189 |
!!!cp ('t3'); |
!!!cp ('t3'); |
| 3190 |
|
# |
| 3191 |
} |
} |
| 3192 |
|
|
| 3193 |
my $doctype = $self->{document}->create_document_type_definition |
my $doctype = $self->{document}->create_document_type_definition |
| 6323 |
} elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) { |
} elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) { |
| 6324 |
!!!cp ('t312'); |
!!!cp ('t312'); |
| 6325 |
!!!parse-error (type => 'after frameset:#text', token => $token); |
!!!parse-error (type => 'after frameset:#text', token => $token); |
| 6326 |
} else { # "after html frameset" |
} else { # "after after frameset" |
| 6327 |
!!!cp ('t313'); |
!!!cp ('t313'); |
| 6328 |
!!!parse-error (type => 'after html:#text', token => $token); |
!!!parse-error (type => 'after html:#text', token => $token); |
|
|
|
|
$self->{insertion_mode} = AFTER_FRAMESET_IM; |
|
|
## Reprocess in the "after frameset" insertion mode. |
|
|
!!!parse-error (type => 'after frameset:#text', token => $token); |
|
| 6329 |
} |
} |
| 6330 |
|
|
| 6331 |
## Ignore the token. |
## Ignore the token. |
| 6341 |
|
|
| 6342 |
die qq[$0: Character "$token->{data}"]; |
die qq[$0: Character "$token->{data}"]; |
| 6343 |
} elsif ($token->{type} == START_TAG_TOKEN) { |
} elsif ($token->{type} == START_TAG_TOKEN) { |
|
if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) { |
|
|
!!!cp ('t316'); |
|
|
!!!parse-error (type => 'after html', |
|
|
text => $token->{tag_name}, token => $token); |
|
|
|
|
|
$self->{insertion_mode} = AFTER_FRAMESET_IM; |
|
|
## Process in the "after frameset" insertion mode. |
|
|
} else { |
|
|
!!!cp ('t317'); |
|
|
} |
|
|
|
|
| 6344 |
if ($token->{tag_name} eq 'frameset' and |
if ($token->{tag_name} eq 'frameset' and |
| 6345 |
$self->{insertion_mode} == IN_FRAMESET_IM) { |
$self->{insertion_mode} == IN_FRAMESET_IM) { |
| 6346 |
!!!cp ('t318'); |
!!!cp ('t318'); |
| 6361 |
## NOTE: As if in head. |
## NOTE: As if in head. |
| 6362 |
$parse_rcdata->(CDATA_CONTENT_MODEL); |
$parse_rcdata->(CDATA_CONTENT_MODEL); |
| 6363 |
next B; |
next B; |
| 6364 |
|
|
| 6365 |
|
## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>| |
| 6366 |
|
## has no parse error. |
| 6367 |
} else { |
} else { |
| 6368 |
if ($self->{insertion_mode} == IN_FRAMESET_IM) { |
if ($self->{insertion_mode} == IN_FRAMESET_IM) { |
| 6369 |
!!!cp ('t321'); |
!!!cp ('t321'); |
| 6370 |
!!!parse-error (type => 'in frameset', |
!!!parse-error (type => 'in frameset', |
| 6371 |
text => $token->{tag_name}, token => $token); |
text => $token->{tag_name}, token => $token); |
| 6372 |
} else { |
} elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) { |
| 6373 |
!!!cp ('t322'); |
!!!cp ('t322'); |
| 6374 |
!!!parse-error (type => 'after frameset', |
!!!parse-error (type => 'after frameset', |
| 6375 |
text => $token->{tag_name}, token => $token); |
text => $token->{tag_name}, token => $token); |
| 6376 |
|
} else { # "after after frameset" |
| 6377 |
|
!!!cp ('t322.2'); |
| 6378 |
|
!!!parse-error (type => 'after after frameset', |
| 6379 |
|
text => $token->{tag_name}, token => $token); |
| 6380 |
} |
} |
| 6381 |
## Ignore the token |
## Ignore the token |
| 6382 |
!!!nack ('t322.1'); |
!!!nack ('t322.1'); |
| 6384 |
next B; |
next B; |
| 6385 |
} |
} |
| 6386 |
} elsif ($token->{type} == END_TAG_TOKEN) { |
} elsif ($token->{type} == END_TAG_TOKEN) { |
|
if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) { |
|
|
!!!cp ('t323'); |
|
|
!!!parse-error (type => 'after html:/', |
|
|
text => $token->{tag_name}, token => $token); |
|
|
|
|
|
$self->{insertion_mode} = AFTER_FRAMESET_IM; |
|
|
## Process in the "after frameset" insertion mode. |
|
|
} else { |
|
|
!!!cp ('t324'); |
|
|
} |
|
|
|
|
| 6387 |
if ($token->{tag_name} eq 'frameset' and |
if ($token->{tag_name} eq 'frameset' and |
| 6388 |
$self->{insertion_mode} == IN_FRAMESET_IM) { |
$self->{insertion_mode} == IN_FRAMESET_IM) { |
| 6389 |
if ($self->{open_elements}->[-1]->[1] & HTML_EL and |
if ($self->{open_elements}->[-1]->[1] & HTML_EL and |
| 6418 |
!!!cp ('t330'); |
!!!cp ('t330'); |
| 6419 |
!!!parse-error (type => 'in frameset:/', |
!!!parse-error (type => 'in frameset:/', |
| 6420 |
text => $token->{tag_name}, token => $token); |
text => $token->{tag_name}, token => $token); |
| 6421 |
} else { |
} elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) { |
| 6422 |
!!!cp ('t331'); |
!!!cp ('t330.1'); |
| 6423 |
!!!parse-error (type => 'after frameset:/', |
!!!parse-error (type => 'after frameset:/', |
| 6424 |
text => $token->{tag_name}, token => $token); |
text => $token->{tag_name}, token => $token); |
| 6425 |
|
} else { # "after after html" |
| 6426 |
|
!!!cp ('t331'); |
| 6427 |
|
!!!parse-error (type => 'after after frameset:/', |
| 6428 |
|
text => $token->{tag_name}, token => $token); |
| 6429 |
} |
} |
| 6430 |
## Ignore the token |
## Ignore the token |
| 6431 |
!!!next-token; |
!!!next-token; |
| 7148 |
!!!cp ('t413'); |
!!!cp ('t413'); |
| 7149 |
!!!parse-error (type => 'unmatched end tag', |
!!!parse-error (type => 'unmatched end tag', |
| 7150 |
text => $token->{tag_name}, token => $token); |
text => $token->{tag_name}, token => $token); |
| 7151 |
|
## NOTE: Ignore the token. |
| 7152 |
} else { |
} else { |
| 7153 |
## Step 1. generate implied end tags |
## Step 1. generate implied end tags |
| 7154 |
while ({ |
while ({ |
| 7208 |
!!!cp ('t421'); |
!!!cp ('t421'); |
| 7209 |
!!!parse-error (type => 'unmatched end tag', |
!!!parse-error (type => 'unmatched end tag', |
| 7210 |
text => $token->{tag_name}, token => $token); |
text => $token->{tag_name}, token => $token); |
| 7211 |
|
## NOTE: Ignore the token. |
| 7212 |
} else { |
} else { |
| 7213 |
## Step 1. generate implied end tags |
## Step 1. generate implied end tags |
| 7214 |
while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) { |
while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) { |
| 7255 |
!!!cp ('t425.1'); |
!!!cp ('t425.1'); |
| 7256 |
!!!parse-error (type => 'unmatched end tag', |
!!!parse-error (type => 'unmatched end tag', |
| 7257 |
text => $token->{tag_name}, token => $token); |
text => $token->{tag_name}, token => $token); |
| 7258 |
|
## NOTE: Ignore the token. |
| 7259 |
} else { |
} else { |
| 7260 |
## Step 1. generate implied end tags |
## Step 1. generate implied end tags |
| 7261 |
while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) { |
while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) { |