| 4 |
|
|
| 5 |
my $HTML_NS = q<http://www.w3.org/1999/xhtml>; |
my $HTML_NS = q<http://www.w3.org/1999/xhtml>; |
| 6 |
|
|
| 7 |
|
## December 2007 HTML5 Classification |
| 8 |
|
|
| 9 |
|
my $HTMLMetadataContent = { |
| 10 |
|
$HTML_NS => { |
| 11 |
|
title => 1, base => 1, link => 1, style => 1, script => 1, noscript => 1, |
| 12 |
|
'event-source' => 1, command => 1, datatemplate => 1, |
| 13 |
|
## NOTE: A |meta| with no |name| element is not allowed as |
| 14 |
|
## a metadata content other than |head| element. |
| 15 |
|
meta => 1, |
| 16 |
|
}, |
| 17 |
|
## NOTE: RDF is mentioned in the HTML5 spec. |
| 18 |
|
## TODO: Other RDF elements? |
| 19 |
|
q<http://www.w3.org/1999/02/22-rdf-syntax-ns#> => {RDF => 1}, |
| 20 |
|
}; |
| 21 |
|
|
| 22 |
|
my $HTMLProseContent = { |
| 23 |
|
$HTML_NS => { |
| 24 |
|
section => 1, nav => 1, article => 1, blockquote => 1, aside => 1, |
| 25 |
|
h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1, header => 1, |
| 26 |
|
footer => 1, address => 1, p => 1, hr => 1, dialog => 1, pre => 1, |
| 27 |
|
ol => 1, ul => 1, dl => 1, figure => 1, map => 1, table => 1, |
| 28 |
|
details => 1, ## ISSUE: "Prose element" in spec. |
| 29 |
|
datagrid => 1, ## ISSUE: "Prose element" in spec. |
| 30 |
|
datatemplate => 1, |
| 31 |
|
div => 1, ## ISSUE: No category in spec. |
| 32 |
|
## NOTE: |style| is only allowed if |scoped| attribute is specified. |
| 33 |
|
## Additionally, it must be before any other element or |
| 34 |
|
## non-inter-element-whitespace text node. |
| 35 |
|
style => 1, |
| 36 |
|
|
| 37 |
|
br => 1, q => 1, cite => 1, em => 1, strong => 1, small => 1, m => 1, |
| 38 |
|
dfn => 1, abbr => 1, time => 1, progress => 1, meter => 1, code => 1, |
| 39 |
|
var => 1, samp => 1, kbd => 1, sub => 1, sup => 1, span => 1, i => 1, |
| 40 |
|
b => 1, bdo => 1, script => 1, noscript => 1, 'event-source' => 1, |
| 41 |
|
command => 1, font => 1, |
| 42 |
|
a => 1, |
| 43 |
|
datagrid => 1, ## ISSUE: "Interactive element" in the spec. |
| 44 |
|
## NOTE: |area| is allowed only as a descendant of |map|. |
| 45 |
|
area => 1, |
| 46 |
|
|
| 47 |
|
ins => 1, del => 1, |
| 48 |
|
|
| 49 |
|
## NOTE: If there is a |menu| ancestor, phrasing. Otherwise, prose. |
| 50 |
|
menu => 1, |
| 51 |
|
|
| 52 |
|
img => 1, iframe => 1, embed => 1, object => 1, video => 1, audio => 1, |
| 53 |
|
canvas => 1, |
| 54 |
|
}, |
| 55 |
|
|
| 56 |
|
## NOTE: Embedded |
| 57 |
|
q<http://www.w3.org/1998/Math/MathML> => {math => 1}, |
| 58 |
|
q<http://www.w3.org/2000/svg> => {svg => 1}, |
| 59 |
|
}; |
| 60 |
|
|
| 61 |
|
my $HTMLSectioningContent = { |
| 62 |
|
$HTML_NS => { |
| 63 |
|
section => 1, nav => 1, article => 1, blockquote => 1, aside => 1, |
| 64 |
|
## NOTE: |body| is only allowed in |html| element. |
| 65 |
|
body => 1, |
| 66 |
|
}, |
| 67 |
|
}; |
| 68 |
|
|
| 69 |
|
my $HTMLHeadingContent = { |
| 70 |
|
$HTML_NS => { |
| 71 |
|
h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1, header => 1, |
| 72 |
|
}, |
| 73 |
|
}; |
| 74 |
|
|
| 75 |
|
my $HTMLPhrasingContent = { |
| 76 |
|
## NOTE: All phrasing content is also prose content. |
| 77 |
|
$HTML_NS => { |
| 78 |
|
br => 1, q => 1, cite => 1, em => 1, strong => 1, small => 1, m => 1, |
| 79 |
|
dfn => 1, abbr => 1, time => 1, progress => 1, meter => 1, code => 1, |
| 80 |
|
var => 1, samp => 1, kbd => 1, sub => 1, sup => 1, span => 1, i => 1, |
| 81 |
|
b => 1, bdo => 1, script => 1, noscript => 1, 'event-source' => 1, |
| 82 |
|
command => 1, font => 1, |
| 83 |
|
a => 1, |
| 84 |
|
datagrid => 1, ## ISSUE: "Interactive element" in the spec. |
| 85 |
|
## NOTE: |area| is allowed only as a descendant of |map|. |
| 86 |
|
area => 1, |
| 87 |
|
|
| 88 |
|
## NOTE: Transparent. |
| 89 |
|
ins => 1, del => 1, |
| 90 |
|
|
| 91 |
|
## NOTE: If there is a |menu| ancestor, phrasing. Otherwise, prose. |
| 92 |
|
menu => 1, |
| 93 |
|
|
| 94 |
|
img => 1, iframe => 1, embed => 1, object => 1, video => 1, audio => 1, |
| 95 |
|
canvas => 1, |
| 96 |
|
}, |
| 97 |
|
|
| 98 |
|
## NOTE: Embedded |
| 99 |
|
q<http://www.w3.org/1998/Math/MathML> => {math => 1}, |
| 100 |
|
q<http://www.w3.org/2000/svg> => {svg => 1}, |
| 101 |
|
|
| 102 |
|
## NOTE: And non-inter-element-whitespace text nodes. |
| 103 |
|
}; |
| 104 |
|
|
| 105 |
|
my $HTMLEmbeddedContent = { |
| 106 |
|
## NOTE: All embedded content is also phrasing content. |
| 107 |
|
$HTML_NS => { |
| 108 |
|
img => 1, iframe => 1, embed => 1, object => 1, video => 1, audio => 1, |
| 109 |
|
canvas => 1, |
| 110 |
|
}, |
| 111 |
|
## NOTE: MathML is mentioned in the HTML5 spec. |
| 112 |
|
q<http://www.w3.org/1998/Math/MathML> => {math => 1}, |
| 113 |
|
## NOTE: SVG is mentioned in the HTML5 spec. |
| 114 |
|
q<http://www.w3.org/2000/svg> => {svg => 1}, |
| 115 |
|
## NOTE: Foreign elements with content (but no metadata) are |
| 116 |
|
## embedded content. |
| 117 |
|
}; |
| 118 |
|
|
| 119 |
|
my $HTMLInteractiveContent = { |
| 120 |
|
$HTML_NS => { |
| 121 |
|
a => 1, |
| 122 |
|
}, |
| 123 |
|
}; |
| 124 |
|
|
| 125 |
|
## Old HTML5 categories |
| 126 |
|
|
| 127 |
my $HTMLMetadataElements = { |
my $HTMLMetadataElements = { |
| 128 |
$HTML_NS => { |
$HTML_NS => { |
| 129 |
qw/link 1 meta 1 style 1 script 1 event-source 1 command 1 base 1 title 1 |
qw/link 1 meta 1 style 1 script 1 event-source 1 command 1 base 1 title 1 |
| 193 |
}, |
}, |
| 194 |
}; # $HTMLSignificantContentErrors |
}; # $HTMLSignificantContentErrors |
| 195 |
|
|
| 196 |
|
## TODO: |
| 197 |
|
|
| 198 |
|
=pod |
| 199 |
|
|
| 200 |
|
As a general rule, elements whose content model allows any |
| 201 |
|
+ <span>prose content</span> should have either at least one |
| 202 |
|
+ descendant text node that is not <span>inter-element |
| 203 |
|
+ whitespace</span>, or at least one descendant element node that is |
| 204 |
|
+ <span>embedded content</span>. For the purposes of this requirement, |
| 205 |
|
+ <code>del</code> elements and their descendants must not be |
| 206 |
|
+ counted as contributing to the ancestors of the <code>del</code> |
| 207 |
|
+ element. |
| 208 |
|
|
| 209 |
|
=cut |
| 210 |
|
|
| 211 |
our $AnyChecker; |
our $AnyChecker; |
| 212 |
my $HTMLAnyChecker = sub { |
my $HTMLAnyChecker = sub { |
| 213 |
my ($self, $todo) = @_; |
my ($self, $todo) = @_; |
| 369 |
return ($new_todos); |
return ($new_todos); |
| 370 |
}; # $HTMLStylableBlockChecker |
}; # $HTMLStylableBlockChecker |
| 371 |
|
|
| 372 |
|
my $HTMLProseContentChecker = sub { |
| 373 |
|
my ($self, $todo) = @_; |
| 374 |
|
my $el = $todo->{node}; |
| 375 |
|
my $new_todos = []; |
| 376 |
|
my @nodes = (@{$el->child_nodes}); |
| 377 |
|
|
| 378 |
|
my $old_values = {significant => |
| 379 |
|
$todo->{flag}->{has_descendant}->{significant}}; |
| 380 |
|
$todo->{flag}->{has_descendant}->{significant} = 0; |
| 381 |
|
|
| 382 |
|
my $has_non_style; |
| 383 |
|
while (@nodes) { |
| 384 |
|
my $node = shift @nodes; |
| 385 |
|
$self->_remove_minuses ($node) and next if ref $node eq 'HASH'; |
| 386 |
|
|
| 387 |
|
my $nt = $node->node_type; |
| 388 |
|
if ($nt == 1) { |
| 389 |
|
my $node_ns = $node->namespace_uri; |
| 390 |
|
$node_ns = '' unless defined $node_ns; |
| 391 |
|
my $node_ln = $node->manakai_local_name; |
| 392 |
|
my $not_allowed = $self->{minuses}->{$node_ns}->{$node_ln}; |
| 393 |
|
if ($node_ns eq $HTML_NS and $node_ln eq 'style') { |
| 394 |
|
$not_allowed = 2 if $has_non_style or |
| 395 |
|
not $node->has_attribute_ns (undef, 'scoped'); |
| 396 |
|
} elsif ($HTMLProseContent->{$node_ns}->{$node_ln}) { |
| 397 |
|
$has_non_style = 1; |
| 398 |
|
if ($HTMLEmbeddedContent->{$node_ns}->{$node_ln}) { |
| 399 |
|
$todo->{flag}->{has_descendant}->{significant} = 1; |
| 400 |
|
} |
| 401 |
|
} elsif ($self->{pluses}->{$node_ns}->{$node_ln}) { |
| 402 |
|
# |
| 403 |
|
} else { |
| 404 |
|
$has_non_style = 1; |
| 405 |
|
$not_allowed = 1; |
| 406 |
|
} |
| 407 |
|
if ($not_allowed) { |
| 408 |
|
if ($not_allowed == 2) { |
| 409 |
|
$self->{onerror}->(node => $node, |
| 410 |
|
type => 'element not allowed:prose style') |
| 411 |
|
} else { |
| 412 |
|
$self->{onerror}->(node => $node, |
| 413 |
|
type => 'element not allowed:prose') |
| 414 |
|
} |
| 415 |
|
} |
| 416 |
|
my ($sib, $ch) = $self->_check_get_children ($node, $todo); |
| 417 |
|
unshift @nodes, @$sib; |
| 418 |
|
push @$new_todos, @$ch; |
| 419 |
|
} elsif ($nt == 3 or $nt == 4) { |
| 420 |
|
if ($node->data =~ /[^\x09-\x0D\x20]/) { |
| 421 |
|
$has_non_style = 1; |
| 422 |
|
$todo->{flag}->{has_descendant}->{significant} = 1; |
| 423 |
|
} |
| 424 |
|
} elsif ($nt == 5) { |
| 425 |
|
unshift @nodes, @{$node->child_nodes}; |
| 426 |
|
} |
| 427 |
|
} |
| 428 |
|
|
| 429 |
|
push @$new_todos, { |
| 430 |
|
type => 'descendant', node => $todo->{node}, flag => $todo->{flag}, |
| 431 |
|
old_values => $old_values, |
| 432 |
|
errors => $HTMLSignificantContentErrors, |
| 433 |
|
}; |
| 434 |
|
|
| 435 |
|
return ($new_todos); |
| 436 |
|
}; # $HTMLProseContentChecker |
| 437 |
|
|
| 438 |
## Zero or more block-level elements |
## Zero or more block-level elements |
| 439 |
my $HTMLBlockChecker = sub { |
my $HTMLBlockChecker = sub { |
| 440 |
my ($self, $todo) = @_; |
my ($self, $todo) = @_; |
| 646 |
return ($new_todos); |
return ($new_todos); |
| 647 |
}; # $HTMLInlineOrStrictlyInlineChecker |
}; # $HTMLInlineOrStrictlyInlineChecker |
| 648 |
|
|
| 649 |
|
my $HTMLPhrasingContentChecker = sub { |
| 650 |
|
my ($self, $todo) = @_; |
| 651 |
|
my $el = $todo->{node}; |
| 652 |
|
my $new_todos = []; |
| 653 |
|
my @nodes = (@{$el->child_nodes}); |
| 654 |
|
|
| 655 |
|
my $old_values = {significant => |
| 656 |
|
$todo->{flag}->{has_descendant}->{significant}}; |
| 657 |
|
$todo->{flag}->{has_descendant}->{significant} = 0; |
| 658 |
|
|
| 659 |
|
while (@nodes) { |
| 660 |
|
my $node = shift @nodes; |
| 661 |
|
$self->_remove_minuses ($node) and next if ref $node eq 'HASH'; |
| 662 |
|
|
| 663 |
|
my $nt = $node->node_type; |
| 664 |
|
if ($nt == 1) { |
| 665 |
|
my $node_ns = $node->namespace_uri; |
| 666 |
|
$node_ns = '' unless defined $node_ns; |
| 667 |
|
my $node_ln = $node->manakai_local_name; |
| 668 |
|
my $not_allowed = $self->{minuses}->{$node_ns}->{$node_ln}; |
| 669 |
|
$not_allowed = 1 |
| 670 |
|
unless $HTMLPhrasingContent->{$node_ns}->{$node_ln} or |
| 671 |
|
$self->{pluses}->{$node_ns}->{$node_ln}; |
| 672 |
|
$self->{onerror}->(node => $node, type => 'element not allowed:phrasing') |
| 673 |
|
if $not_allowed; |
| 674 |
|
my ($sib, $ch) = $self->_check_get_children ($node, $todo); |
| 675 |
|
unshift @nodes, @$sib; |
| 676 |
|
push @$new_todos, @$ch; |
| 677 |
|
} elsif ($nt == 3 or $nt == 4) { |
| 678 |
|
if ($node->data =~ /[^\x09-\x0D\x20]/) { |
| 679 |
|
$todo->{flag}->{has_descendant}->{significant} = 1; |
| 680 |
|
} |
| 681 |
|
} elsif ($nt == 5) { |
| 682 |
|
unshift @nodes, @{$node->child_nodes}; |
| 683 |
|
} |
| 684 |
|
} |
| 685 |
|
|
| 686 |
|
push @$new_todos, { |
| 687 |
|
type => 'descendant', node => $todo->{node}, flag => $todo->{flag}, |
| 688 |
|
old_values => $old_values, |
| 689 |
|
errors => $HTMLSignificantContentErrors, |
| 690 |
|
}; |
| 691 |
|
|
| 692 |
|
return ($new_todos); |
| 693 |
|
}; # $HTMLPhrasingContentChecker |
| 694 |
|
|
| 695 |
## Block-level content or inline-level content (i.e. bimorphic content model) |
## Block-level content or inline-level content (i.e. bimorphic content model) |
| 696 |
my $HTMLBlockOrInlineChecker = sub { |
my $HTMLBlockOrInlineChecker = sub { |
| 697 |
my ($self, $todo) = @_; |
my ($self, $todo) = @_; |
| 879 |
}; |
}; |
| 880 |
}; # $GetHTMLZeroOrMoreThenBlockOrInlineChecker |
}; # $GetHTMLZeroOrMoreThenBlockOrInlineChecker |
| 881 |
|
|
| 882 |
my $HTMLTransparentChecker = $HTMLBlockOrInlineChecker; |
my $HTMLTransparentChecker = $HTMLProseContentChecker; |
| 883 |
## ISSUE: Significant content rule should be applied to transparent element |
## ISSUE: Significant content rule should be applied to transparent element |
| 884 |
## with parent? Currently, applied to |video| but not to others. |
## with parent? Currently, applied to |video| but not to others. |
| 885 |
|
|
| 1217 |
type => 'reserved browsing context name'); |
type => 'reserved browsing context name'); |
| 1218 |
} |
} |
| 1219 |
} else { |
} else { |
| 1220 |
#$ ISSUE: An empty string is conforming? |
## NOTE: An empty string is a valid browsing context name (same as _self). |
| 1221 |
} |
} |
| 1222 |
}; # $HTMLTargetAttrChecker |
}; # $HTMLTargetAttrChecker |
| 1223 |
|
|
| 1469 |
my @nodes = (@{$el->child_nodes}); |
my @nodes = (@{$el->child_nodes}); |
| 1470 |
|
|
| 1471 |
my $has_title; |
my $has_title; |
|
my $phase = 'initial'; # 'after charset', 'after base' |
|
| 1472 |
while (@nodes) { |
while (@nodes) { |
| 1473 |
my $node = shift @nodes; |
my $node = shift @nodes; |
| 1474 |
$self->_remove_minuses ($node) and next if ref $node eq 'HASH'; |
$self->_remove_minuses ($node) and next if ref $node eq 'HASH'; |
| 1482 |
if ($self->{pluses}->{$node_ns}->{$node_ln}) { |
if ($self->{pluses}->{$node_ns}->{$node_ln}) { |
| 1483 |
# |
# |
| 1484 |
} elsif ($node_ns eq $HTML_NS and $node_ln eq 'title') { |
} elsif ($node_ns eq $HTML_NS and $node_ln eq 'title') { |
|
$phase = 'after base'; |
|
| 1485 |
unless ($has_title) { |
unless ($has_title) { |
| 1486 |
$has_title = 1; |
$has_title = 1; |
| 1487 |
} else { |
} else { |
| 1488 |
$not_allowed = 1; |
$not_allowed = 1; |
| 1489 |
} |
} |
| 1490 |
} elsif ($node_ns eq $HTML_NS and $node_ln eq 'meta') { |
} elsif ($HTMLMetadataContent->{$node_ns}->{$node_ln}) { |
| 1491 |
if ($node->has_attribute_ns (undef, 'charset')) { |
# |
| 1492 |
if ($phase eq 'initial') { |
|
| 1493 |
$phase = 'after charset'; |
## NOTE: |meta| is a metadata content. However, strictly speaking, |
| 1494 |
} else { |
## a |meta| element with none of |charset|, |name|, |
| 1495 |
$not_allowed = 1; |
## or |http-equiv| attribute is not allowed. It is non-conforming |
| 1496 |
## NOTE: See also |base|'s "contexts" field in the spec |
## anyway. |
|
} |
|
|
} elsif ($node->has_attribute_ns (undef, 'name') or |
|
|
$node->has_attribute_ns (undef, 'http-equiv')) { |
|
|
$phase = 'after base'; |
|
|
} else { |
|
|
$phase = 'after base'; |
|
|
$not_allowed = 1; |
|
|
} |
|
|
} elsif ($node_ns eq $HTML_NS and $node_ln eq 'base') { |
|
|
if ($phase eq 'initial' or $phase eq 'after charset') { |
|
|
$phase = 'after base'; |
|
|
} else { |
|
|
$not_allowed = 1; |
|
|
} |
|
|
} elsif ($node_ns eq $HTML_NS and $node_ln eq 'style') { |
|
|
$phase = 'after base'; |
|
|
if ($node->has_attribute_ns (undef, 'scoped')) { |
|
|
$not_allowed = 1; |
|
|
} |
|
|
} elsif ($HTMLMetadataElements->{$node_ns}->{$node_ln}) { |
|
|
$phase = 'after base'; |
|
| 1497 |
} else { |
} else { |
| 1498 |
$not_allowed = 1; |
$not_allowed = 1; |
| 1499 |
} |
} |
| 1528 |
attrs_checker => sub { |
attrs_checker => sub { |
| 1529 |
my ($self, $todo) = @_; |
my ($self, $todo) = @_; |
| 1530 |
|
|
| 1531 |
|
if ($self->{has_base}) { |
| 1532 |
|
$self->{onerror}->(node => $todo->{node}, |
| 1533 |
|
type => 'element not allowed:base'); |
| 1534 |
|
} else { |
| 1535 |
|
$self->{has_base} = 1; |
| 1536 |
|
} |
| 1537 |
|
|
| 1538 |
my $has_href = $todo->{node}->has_attribute_ns (undef, 'href'); |
my $has_href = $todo->{node}->has_attribute_ns (undef, 'href'); |
| 1539 |
my $has_target = $todo->{node}->has_attribute_ns (undef, 'target'); |
my $has_target = $todo->{node}->has_attribute_ns (undef, 'target'); |
| 1540 |
|
|
| 1545 |
## <title xml:base="relative"/><base href/> (maybe it should be) |
## <title xml:base="relative"/><base href/> (maybe it should be) |
| 1546 |
## <unknown xmlns="relative"/><base href/> (assuming that |
## <unknown xmlns="relative"/><base href/> (assuming that |
| 1547 |
## |{relative}:unknown| is allowed before XHTML |base| (unlikely, though)) |
## |{relative}:unknown| is allowed before XHTML |base| (unlikely, though)) |
|
## <?xml-stylesheet href="relative"?>...<base href=""/> |
|
|
## NOTE: These are non-conformant anyway because of |head|'s content model: |
|
| 1548 |
## <style>@import 'relative';</style><base href> |
## <style>@import 'relative';</style><base href> |
| 1549 |
## <script>location.href = 'relative';</script><base href> |
## <script>location.href = 'relative';</script><base href> |
| 1550 |
## NOTE: <html manifest=".."><head><base href=""/> is conforming as |
## NOTE: <html manifest=".."><head><base href=""/> is conforming as |
| 1710 |
} |
} |
| 1711 |
|
|
| 1712 |
if (defined $charset_attr) { |
if (defined $charset_attr) { |
| 1713 |
|
my $parent = $todo->{node}->manakai_parent_element; |
| 1714 |
|
if ($parent and $parent eq $parent->owner_document->manakai_head) { |
| 1715 |
|
for my $el (@{$parent->child_nodes}) { |
| 1716 |
|
next unless $el->node_type == 1; # ELEMENT_NODE |
| 1717 |
|
unless ($el eq $todo->{node}) { |
| 1718 |
|
## NOTE: Not the first child element. |
| 1719 |
|
$self->{onerror}->(node => $todo->{node}, |
| 1720 |
|
type => 'element not allowed:meta charset'); |
| 1721 |
|
} |
| 1722 |
|
last; |
| 1723 |
|
## NOTE: Entity references are not supported. |
| 1724 |
|
} |
| 1725 |
|
} else { |
| 1726 |
|
$self->{onerror}->(node => $todo->{node}, |
| 1727 |
|
type => 'element not allowed:meta charset'); |
| 1728 |
|
} |
| 1729 |
|
|
| 1730 |
unless ($todo->{node}->owner_document->manakai_is_html) { |
unless ($todo->{node}->owner_document->manakai_is_html) { |
| 1731 |
$self->{onerror}->(node => $charset_attr, |
$self->{onerror}->(node => $charset_attr, |
| 1732 |
type => 'in XML:charset'); |
type => 'in XML:charset'); |
| 1840 |
|
|
| 1841 |
my $nt = $node->node_type; |
my $nt = $node->node_type; |
| 1842 |
if ($nt == 1) { |
if ($nt == 1) { |
| 1843 |
$self->{onerror}->(node => $node, type => 'element not allowed'); |
my $node_ns = $node->namespace_uri; |
| 1844 |
|
$node_ns = '' unless defined $node_ns; |
| 1845 |
|
my $node_ln = $node->manakai_local_name; |
| 1846 |
|
if ($self->{pluses}->{$node_ns}->{$node_ln}) { |
| 1847 |
|
# |
| 1848 |
|
} else { |
| 1849 |
|
$self->{onerror}->(node => $node, type => 'element not allowed'); |
| 1850 |
|
} |
| 1851 |
my ($sib, $ch) = $self->_check_get_children ($node, $todo); |
my ($sib, $ch) = $self->_check_get_children ($node, $todo); |
| 1852 |
unshift @nodes, @$sib; |
unshift @nodes, @$sib; |
| 1853 |
push @$new_todos, @$ch; |
push @$new_todos, @$ch; |
| 1872 |
|
|
| 1873 |
$Element->{$HTML_NS}->{body} = { |
$Element->{$HTML_NS}->{body} = { |
| 1874 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 1875 |
checker => $HTMLBlockChecker, |
checker => $HTMLProseContentChecker, |
| 1876 |
}; |
}; |
| 1877 |
|
|
| 1878 |
$Element->{$HTML_NS}->{section} = { |
$Element->{$HTML_NS}->{section} = { |
| 1879 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 1880 |
checker => $HTMLStylableBlockChecker, |
checker => $HTMLProseContentChecker, |
| 1881 |
}; |
}; |
| 1882 |
|
|
| 1883 |
$Element->{$HTML_NS}->{nav} = { |
$Element->{$HTML_NS}->{nav} = { |
| 1884 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 1885 |
checker => $HTMLBlockOrInlineChecker, |
checker => $HTMLProseContentChecker, |
| 1886 |
}; |
}; |
| 1887 |
|
|
| 1888 |
$Element->{$HTML_NS}->{article} = { |
$Element->{$HTML_NS}->{article} = { |
| 1889 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 1890 |
checker => $HTMLStylableBlockChecker, |
checker => $HTMLProseContentChecker, |
| 1891 |
}; |
}; |
| 1892 |
|
|
| 1893 |
$Element->{$HTML_NS}->{blockquote} = { |
$Element->{$HTML_NS}->{blockquote} = { |
| 1894 |
attrs_checker => $GetHTMLAttrsChecker->({ |
attrs_checker => $GetHTMLAttrsChecker->({ |
| 1895 |
cite => $HTMLURIAttrChecker, |
cite => $HTMLURIAttrChecker, |
| 1896 |
}), |
}), |
| 1897 |
checker => $HTMLBlockChecker, |
checker => $HTMLProseContentChecker, |
| 1898 |
}; |
}; |
| 1899 |
|
|
| 1900 |
$Element->{$HTML_NS}->{aside} = { |
$Element->{$HTML_NS}->{aside} = { |
| 1901 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 1902 |
checker => $GetHTMLZeroOrMoreThenBlockOrInlineChecker->($HTML_NS, 'style'), |
checker => $HTMLProseContentChecker, |
| 1903 |
}; |
}; |
| 1904 |
|
|
| 1905 |
$Element->{$HTML_NS}->{h1} = { |
$Element->{$HTML_NS}->{h1} = { |
| 1907 |
checker => sub { |
checker => sub { |
| 1908 |
my ($self, $todo) = @_; |
my ($self, $todo) = @_; |
| 1909 |
$todo->{flag}->{has_descendant}->{hn} = 1; |
$todo->{flag}->{has_descendant}->{hn} = 1; |
| 1910 |
return $HTMLStrictlyInlineChecker->($self, $todo); |
return $HTMLPhrasingContentChecker->($self, $todo); |
| 1911 |
}, |
}, |
| 1912 |
}; |
}; |
| 1913 |
|
|
| 1936 |
checker => $Element->{$HTML_NS}->{h1}->{checker}, |
checker => $Element->{$HTML_NS}->{h1}->{checker}, |
| 1937 |
}; |
}; |
| 1938 |
|
|
| 1939 |
|
## TODO: Explicit sectioning is "encouraged". |
| 1940 |
|
|
| 1941 |
$Element->{$HTML_NS}->{header} = { |
$Element->{$HTML_NS}->{header} = { |
| 1942 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 1943 |
checker => sub { |
checker => sub { |
| 1948 |
|
|
| 1949 |
my $end = $self->_add_minuses |
my $end = $self->_add_minuses |
| 1950 |
({$HTML_NS => {qw/header 1 footer 1/}}, |
({$HTML_NS => {qw/header 1 footer 1/}}, |
| 1951 |
$HTMLSectioningElements); |
$HTMLSectioningContent); |
| 1952 |
my ($new_todos, $ch) = $HTMLBlockChecker->($self, $todo); |
my ($new_todos, $ch) = $HTMLProseContentChecker->($self, $todo); |
| 1953 |
push @$new_todos, $end, |
push @$new_todos, $end, |
| 1954 |
{type => 'descendant', node => $todo->{node}, |
{type => 'descendant', node => $todo->{node}, |
| 1955 |
flag => $todo->{flag}, old_values => $old_flags, |
flag => $todo->{flag}, old_values => $old_flags, |
| 1968 |
|
|
| 1969 |
$Element->{$HTML_NS}->{footer} = { |
$Element->{$HTML_NS}->{footer} = { |
| 1970 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 1971 |
checker => sub { ## block -hn -header -footer -sectioning or inline |
checker => sub { |
| 1972 |
my ($self, $todo) = @_; |
my ($self, $todo) = @_; |
|
my $el = $todo->{node}; |
|
|
my $new_todos = []; |
|
|
my @nodes = (@{$el->child_nodes}); |
|
| 1973 |
|
|
| 1974 |
my $old_values = {significant => |
my $old_flags = {hn => $todo->{flag}->{has_descendant}->{hn}}; |
| 1975 |
$todo->{flag}->{has_descendant}->{significant}}; |
$todo->{flag}->{has_descendant}->{hn} = 0; |
|
$todo->{flag}->{has_descendant}->{significant} = 0; |
|
|
|
|
|
my $content = 'block-or-inline'; # or 'block' or 'inline' |
|
|
my @block_not_inline; |
|
|
while (@nodes) { |
|
|
my $node = shift @nodes; |
|
|
$self->_remove_minuses ($node) and next if ref $node eq 'HASH'; |
|
|
|
|
|
my $nt = $node->node_type; |
|
|
if ($nt == 1) { |
|
|
my $node_ns = $node->namespace_uri; |
|
|
$node_ns = '' unless defined $node_ns; |
|
|
my $node_ln = $node->manakai_local_name; |
|
|
my $not_allowed; |
|
|
if ($self->{minuses}->{$node_ns}->{$node_ln}) { |
|
|
$not_allowed = 1; |
|
|
} elsif ($node_ns eq $HTML_NS and |
|
|
{ |
|
|
qw/h1 1 h2 1 h3 1 h4 1 h5 1 h6 1 header 1 footer 1/ |
|
|
}->{$node_ln}) { |
|
|
$not_allowed = 1; |
|
|
} elsif ($HTMLSectioningElements->{$node_ns}->{$node_ln}) { |
|
|
$not_allowed = 1; |
|
|
} |
|
|
if ($content eq 'block') { |
|
|
$not_allowed = 1 |
|
|
unless $HTMLBlockLevelElements->{$node_ns}->{$node_ln} or |
|
|
$self->{pluses}->{$node_ns}->{$node_ln}; |
|
|
} elsif ($content eq 'inline') { |
|
|
$not_allowed = 1 |
|
|
unless $HTMLStrictlyInlineLevelElements->{$node_ns}->{$node_ln} or |
|
|
$HTMLStructuredInlineLevelElements->{$node_ns}->{$node_ln} or |
|
|
$self->{pluses}->{$node_ns}->{$node_ln}; |
|
|
} else { |
|
|
my $is_block = $HTMLBlockLevelElements->{$node_ns}->{$node_ln}; |
|
|
my $is_inline |
|
|
= $HTMLStrictlyInlineLevelElements->{$node_ns}->{$node_ln} || |
|
|
$HTMLStructuredInlineLevelElements->{$node_ns}->{$node_ln}; |
|
|
|
|
|
push @block_not_inline, $node |
|
|
if $is_block and not $is_inline and not $not_allowed; |
|
|
if (not $is_block and not $self->{pluses}->{$node_ns}->{$node_ln}) { |
|
|
$content = 'inline'; |
|
|
for (@block_not_inline) { |
|
|
$self->{onerror}->(node => $_, type => 'element not allowed'); |
|
|
} |
|
|
$not_allowed = 1 unless $is_inline; |
|
|
} |
|
|
} |
|
|
$self->{onerror}->(node => $node, type => 'element not allowed') |
|
|
if $not_allowed; |
|
|
my ($sib, $ch) = $self->_check_get_children ($node, $todo); |
|
|
unshift @nodes, @$sib; |
|
|
push @$new_todos, @$ch; |
|
|
} elsif ($nt == 3 or $nt == 4) { |
|
|
if ($node->data =~ /[^\x09-\x0D\x20]/) { |
|
|
if ($content eq 'block') { |
|
|
$self->{onerror}->(node => $node, type => 'character not allowed'); |
|
|
} else { |
|
|
$content = 'inline'; |
|
|
for (@block_not_inline) { |
|
|
$self->{onerror}->(node => $_, type => 'element not allowed'); |
|
|
} |
|
|
} |
|
|
$todo->{flag}->{has_descendant}->{significant} = 1; |
|
|
} |
|
|
} elsif ($nt == 5) { |
|
|
unshift @nodes, @{$node->child_nodes}; |
|
|
} |
|
|
} |
|
| 1976 |
|
|
| 1977 |
my $end = $self->_add_minuses |
my $end = $self->_add_minuses |
| 1978 |
({$HTML_NS => {qw/h1 1 h2 1 h3 1 h4 1 h5 1 h6 1/}}, |
({$HTML_NS => {footer => 1}}, |
| 1979 |
$HTMLSectioningElements); |
$HTMLSectioningContent, $HTMLHeadingContent); |
| 1980 |
|
my ($new_todos, $ch) = $HTMLProseContentChecker->($self, $todo); |
| 1981 |
push @$new_todos, $end; |
push @$new_todos, $end; |
| 1982 |
|
|
| 1983 |
if ($content eq 'inline') { |
return ($new_todos, $ch); |
|
for (@$new_todos) { |
|
|
$_->{inline} = 1; |
|
|
} |
|
|
} |
|
|
|
|
|
push @$new_todos, { |
|
|
type => 'descendant', node => $todo->{node}, flag => $todo->{flag}, |
|
|
old_values => $old_values, |
|
|
errors => $HTMLSignificantContentErrors, |
|
|
}; |
|
|
|
|
|
return ($new_todos); |
|
| 1984 |
}, |
}, |
| 1985 |
}; |
}; |
| 1986 |
|
|
| 1987 |
$Element->{$HTML_NS}->{address} = { |
$Element->{$HTML_NS}->{address} = { |
| 1988 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 1989 |
checker => $HTMLInlineChecker, |
checker => sub { |
| 1990 |
|
my ($self, $todo) = @_; |
| 1991 |
|
|
| 1992 |
|
my $old_flags = {hn => $todo->{flag}->{has_descendant}->{hn}}; |
| 1993 |
|
$todo->{flag}->{has_descendant}->{hn} = 0; |
| 1994 |
|
|
| 1995 |
|
my $end = $self->_add_minuses |
| 1996 |
|
({$HTML_NS => {footer => 1, address => 1}}, |
| 1997 |
|
$HTMLSectioningContent, $HTMLHeadingContent); |
| 1998 |
|
my ($new_todos, $ch) = $HTMLProseContentChecker->($self, $todo); |
| 1999 |
|
push @$new_todos, $end; |
| 2000 |
|
|
| 2001 |
|
return ($new_todos, $ch); |
| 2002 |
|
}, |
| 2003 |
}; |
}; |
| 2004 |
|
|
| 2005 |
$Element->{$HTML_NS}->{p} = { |
$Element->{$HTML_NS}->{p} = { |
| 2006 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 2007 |
checker => $HTMLInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2008 |
}; |
}; |
| 2009 |
|
|
| 2010 |
$Element->{$HTML_NS}->{hr} = { |
$Element->{$HTML_NS}->{hr} = { |
| 2015 |
$Element->{$HTML_NS}->{br} = { |
$Element->{$HTML_NS}->{br} = { |
| 2016 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 2017 |
checker => $HTMLEmptyChecker, |
checker => $HTMLEmptyChecker, |
| 2018 |
|
## NOTE: Blank line MUST NOT be used for presentation purpose. |
| 2019 |
|
## (This requirement is semantic so that we cannot check.) |
| 2020 |
}; |
}; |
| 2021 |
|
|
| 2022 |
$Element->{$HTML_NS}->{dialog} = { |
$Element->{$HTML_NS}->{dialog} = { |
| 2082 |
|
|
| 2083 |
$Element->{$HTML_NS}->{pre} = { |
$Element->{$HTML_NS}->{pre} = { |
| 2084 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 2085 |
checker => $HTMLStrictlyInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2086 |
}; |
}; |
| 2087 |
|
|
| 2088 |
$Element->{$HTML_NS}->{ol} = { |
$Element->{$HTML_NS}->{ol} = { |
| 2156 |
}), |
}), |
| 2157 |
checker => sub { |
checker => sub { |
| 2158 |
my ($self, $todo) = @_; |
my ($self, $todo) = @_; |
| 2159 |
if ($todo->{inline}) { |
if ($todo->{flag}->{in_menu}) { |
| 2160 |
return $HTMLInlineChecker->($self, $todo); |
return $HTMLPhrasingContentChecker->($self, $todo); |
| 2161 |
} else { |
} else { |
| 2162 |
return $HTMLBlockOrInlineChecker->($self, $todo); |
return $HTMLProseContentChecker->($self, $todo); |
| 2163 |
} |
} |
| 2164 |
}, |
}, |
| 2165 |
}; |
}; |
| 2239 |
|
|
| 2240 |
$Element->{$HTML_NS}->{dt} = { |
$Element->{$HTML_NS}->{dt} = { |
| 2241 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 2242 |
checker => $HTMLStrictlyInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2243 |
}; |
}; |
| 2244 |
|
|
| 2245 |
$Element->{$HTML_NS}->{dd} = { |
$Element->{$HTML_NS}->{dd} = { |
| 2246 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 2247 |
checker => $Element->{$HTML_NS}->{li}->{checker}, |
checker => $HTMLProseContentChecker, |
| 2248 |
}; |
}; |
| 2249 |
|
|
| 2250 |
$Element->{$HTML_NS}->{a} = { |
$Element->{$HTML_NS}->{a} = { |
| 2298 |
my ($self, $todo) = @_; |
my ($self, $todo) = @_; |
| 2299 |
|
|
| 2300 |
my $end = $self->_add_minuses ($HTMLInteractiveElements); |
my $end = $self->_add_minuses ($HTMLInteractiveElements); |
| 2301 |
my ($new_todos, $ch) |
my ($new_todos, $ch) = $HTMLPhrasingContentChecker->($self, $todo); |
|
= $HTMLInlineOrStrictlyInlineChecker->($self, $todo); |
|
| 2302 |
push @$new_todos, $end; |
push @$new_todos, $end; |
| 2303 |
|
|
| 2304 |
if ($todo->{node}->has_attribute_ns (undef, 'href')) { |
if ($todo->{node}->has_attribute_ns (undef, 'href')) { |
| 2313 |
attrs_checker => $GetHTMLAttrsChecker->({ |
attrs_checker => $GetHTMLAttrsChecker->({ |
| 2314 |
cite => $HTMLURIAttrChecker, |
cite => $HTMLURIAttrChecker, |
| 2315 |
}), |
}), |
| 2316 |
checker => $HTMLInlineOrStrictlyInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2317 |
}; |
}; |
| 2318 |
|
|
| 2319 |
$Element->{$HTML_NS}->{cite} = { |
$Element->{$HTML_NS}->{cite} = { |
| 2320 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 2321 |
checker => $HTMLStrictlyInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2322 |
}; |
}; |
| 2323 |
|
|
| 2324 |
$Element->{$HTML_NS}->{em} = { |
$Element->{$HTML_NS}->{em} = { |
| 2325 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 2326 |
checker => $HTMLInlineOrStrictlyInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2327 |
}; |
}; |
| 2328 |
|
|
| 2329 |
$Element->{$HTML_NS}->{strong} = { |
$Element->{$HTML_NS}->{strong} = { |
| 2330 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 2331 |
checker => $HTMLInlineOrStrictlyInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2332 |
}; |
}; |
| 2333 |
|
|
| 2334 |
$Element->{$HTML_NS}->{small} = { |
$Element->{$HTML_NS}->{small} = { |
| 2335 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 2336 |
checker => $HTMLInlineOrStrictlyInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2337 |
}; |
}; |
| 2338 |
|
|
| 2339 |
$Element->{$HTML_NS}->{m} = { |
$Element->{$HTML_NS}->{m} = { |
| 2340 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 2341 |
checker => $HTMLInlineOrStrictlyInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2342 |
}; |
}; |
| 2343 |
|
|
| 2344 |
$Element->{$HTML_NS}->{dfn} = { |
$Element->{$HTML_NS}->{dfn} = { |
| 2347 |
my ($self, $todo) = @_; |
my ($self, $todo) = @_; |
| 2348 |
|
|
| 2349 |
my $end = $self->_add_minuses ({$HTML_NS => {dfn => 1}}); |
my $end = $self->_add_minuses ({$HTML_NS => {dfn => 1}}); |
| 2350 |
my ($sib, $ch) = $HTMLStrictlyInlineChecker->($self, $todo); |
my ($sib, $ch) = $HTMLPhrasingContentChecker->($self, $todo); |
| 2351 |
push @$sib, $end; |
push @$sib, $end; |
| 2352 |
|
|
| 2353 |
my $node = $todo->{node}; |
my $node = $todo->{node}; |
| 2400 |
## if there is a |dfn| whose defining term is the abbreviation, |
## if there is a |dfn| whose defining term is the abbreviation, |
| 2401 |
## but it does not prohibit |abbr| w/o |title| in other cases. |
## but it does not prohibit |abbr| w/o |title| in other cases. |
| 2402 |
}), |
}), |
| 2403 |
checker => $HTMLStrictlyInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2404 |
}; |
}; |
| 2405 |
|
|
| 2406 |
$Element->{$HTML_NS}->{time} = { |
$Element->{$HTML_NS}->{time} = { |
| 2530 |
type => 'dateortime:syntax error'); |
type => 'dateortime:syntax error'); |
| 2531 |
} |
} |
| 2532 |
|
|
| 2533 |
return $HTMLStrictlyInlineChecker->($self, $todo); |
return $HTMLPhrasingContentChecker->($self, $todo); |
| 2534 |
}, |
}, |
| 2535 |
}; |
}; |
| 2536 |
|
|
| 2543 |
max => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }), |
max => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }), |
| 2544 |
optimum => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }), |
optimum => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }), |
| 2545 |
}), |
}), |
| 2546 |
checker => $HTMLStrictlyInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2547 |
}; |
}; |
| 2548 |
|
|
| 2549 |
$Element->{$HTML_NS}->{progress} = { ## TODO: recommended to use content |
$Element->{$HTML_NS}->{progress} = { ## TODO: recommended to use content |
| 2551 |
value => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift >= 0 }), |
value => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift >= 0 }), |
| 2552 |
max => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift > 0 }), |
max => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift > 0 }), |
| 2553 |
}), |
}), |
| 2554 |
checker => $HTMLStrictlyInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2555 |
}; |
}; |
| 2556 |
|
|
| 2557 |
$Element->{$HTML_NS}->{code} = { |
$Element->{$HTML_NS}->{code} = { |
| 2558 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 2559 |
## NOTE: Though |title| has special semantics, |
## NOTE: Though |title| has special semantics, |
| 2560 |
## syntatically same as the |title| as global attribute. |
## syntatically same as the |title| as global attribute. |
| 2561 |
checker => $HTMLInlineOrStrictlyInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2562 |
}; |
}; |
| 2563 |
|
|
| 2564 |
$Element->{$HTML_NS}->{var} = { |
$Element->{$HTML_NS}->{var} = { |
| 2565 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 2566 |
## NOTE: Though |title| has special semantics, |
## NOTE: Though |title| has special semantics, |
| 2567 |
## syntatically same as the |title| as global attribute. |
## syntatically same as the |title| as global attribute. |
| 2568 |
checker => $HTMLStrictlyInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2569 |
}; |
}; |
| 2570 |
|
|
| 2571 |
$Element->{$HTML_NS}->{samp} = { |
$Element->{$HTML_NS}->{samp} = { |
| 2572 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 2573 |
## NOTE: Though |title| has special semantics, |
## NOTE: Though |title| has special semantics, |
| 2574 |
## syntatically same as the |title| as global attribute. |
## syntatically same as the |title| as global attribute. |
| 2575 |
checker => $HTMLInlineOrStrictlyInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2576 |
}; |
}; |
| 2577 |
|
|
| 2578 |
$Element->{$HTML_NS}->{kbd} = { |
$Element->{$HTML_NS}->{kbd} = { |
| 2579 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 2580 |
checker => $HTMLStrictlyInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2581 |
}; |
}; |
| 2582 |
|
|
| 2583 |
$Element->{$HTML_NS}->{sub} = { |
$Element->{$HTML_NS}->{sub} = { |
| 2584 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 2585 |
checker => $HTMLStrictlyInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2586 |
}; |
}; |
| 2587 |
|
|
| 2588 |
$Element->{$HTML_NS}->{sup} = { |
$Element->{$HTML_NS}->{sup} = { |
| 2589 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 2590 |
checker => $HTMLStrictlyInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2591 |
}; |
}; |
| 2592 |
|
|
| 2593 |
$Element->{$HTML_NS}->{span} = { |
$Element->{$HTML_NS}->{span} = { |
| 2594 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 2595 |
## NOTE: Though |title| has special semantics, |
## NOTE: Though |title| has special semantics, |
| 2596 |
## syntatically same as the |title| as global attribute. |
## syntatically same as the |title| as global attribute. |
| 2597 |
checker => $HTMLInlineOrStrictlyInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2598 |
}; |
}; |
| 2599 |
|
|
| 2600 |
$Element->{$HTML_NS}->{i} = { |
$Element->{$HTML_NS}->{i} = { |
| 2601 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 2602 |
## NOTE: Though |title| has special semantics, |
## NOTE: Though |title| has special semantics, |
| 2603 |
## syntatically same as the |title| as global attribute. |
## syntatically same as the |title| as global attribute. |
| 2604 |
checker => $HTMLStrictlyInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2605 |
}; |
}; |
| 2606 |
|
|
| 2607 |
$Element->{$HTML_NS}->{b} = { |
$Element->{$HTML_NS}->{b} = { |
| 2608 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 2609 |
checker => $HTMLStrictlyInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2610 |
}; |
}; |
| 2611 |
|
|
| 2612 |
$Element->{$HTML_NS}->{bdo} = { |
$Element->{$HTML_NS}->{bdo} = { |
| 2618 |
} |
} |
| 2619 |
}, |
}, |
| 2620 |
## ISSUE: The spec does not directly say that |dir| is a enumerated attr. |
## ISSUE: The spec does not directly say that |dir| is a enumerated attr. |
| 2621 |
checker => $HTMLStrictlyInlineChecker, |
checker => $HTMLPhrasingContentChecker, |
| 2622 |
}; |
}; |
| 2623 |
|
|
| 2624 |
|
=pod |
| 2625 |
|
|
| 2626 |
|
## TODO: |
| 2627 |
|
|
| 2628 |
|
+ |
| 2629 |
|
+ <p>Partly because of the confusion described above, authors are |
| 2630 |
|
+ strongly recommended to always mark up all paragraphs with the |
| 2631 |
|
+ <code>p</code> element, and to not have any <code>ins</code> or |
| 2632 |
|
+ <code>del</code> elements that cross across any <span |
| 2633 |
|
+ title="paragraph">implied paragraphs</span>.</p> |
| 2634 |
|
+ |
| 2635 |
|
(An informative note) |
| 2636 |
|
|
| 2637 |
|
<p><code>ins</code> elements should not cross <span |
| 2638 |
|
+ title="paragraph">implied paragraph</span> boundaries.</p> |
| 2639 |
|
(normative) |
| 2640 |
|
|
| 2641 |
|
+ <p><code>del</code> elements should not cross <span |
| 2642 |
|
+ title="paragraph">implied paragraph</span> boundaries.</p> |
| 2643 |
|
(normative) |
| 2644 |
|
|
| 2645 |
|
=cut |
| 2646 |
|
|
| 2647 |
$Element->{$HTML_NS}->{ins} = { |
$Element->{$HTML_NS}->{ins} = { |
| 2648 |
attrs_checker => $GetHTMLAttrsChecker->({ |
attrs_checker => $GetHTMLAttrsChecker->({ |
| 2649 |
cite => $HTMLURIAttrChecker, |
cite => $HTMLURIAttrChecker, |
| 2659 |
}), |
}), |
| 2660 |
checker => sub { |
checker => sub { |
| 2661 |
my ($self, $todo) = @_; |
my ($self, $todo) = @_; |
| 2662 |
|
my $sig_flag = $todo->{flag}->{has_descendant}->{significant}; |
| 2663 |
my $parent = $todo->{node}->manakai_parent_element; |
my ($new_todos) = $HTMLTransparentChecker->($self, $todo); |
| 2664 |
if (defined $parent) { |
push @$new_todos, {type => 'code', code => sub { |
| 2665 |
my $sig_flag = $todo->{flag}->{has_descendant}->{significant}; |
$todo->{flag}->{has_descendant}->{significant} = 0; |
| 2666 |
my $nsuri = $parent->namespace_uri; |
}} if not $sig_flag; |
| 2667 |
$nsuri = '' unless defined $nsuri; |
return $new_todos; |
|
my $ln = $parent->manakai_local_name; |
|
|
my $eldef = $Element->{$nsuri}->{$ln} || |
|
|
$Element->{$nsuri}->{''} || |
|
|
$ElementDefault; |
|
|
my ($new_todos) = $eldef->{checker}->($self, $todo); |
|
|
push @$new_todos, {type => 'code', code => sub { |
|
|
$todo->{flag}->{has_descendant}->{significant} = 0; |
|
|
}} if not $sig_flag; |
|
|
return $new_todos; |
|
|
} else { |
|
|
return $HTMLBlockOrInlineChecker->($self, $todo); |
|
|
} |
|
| 2668 |
}, |
}, |
| 2669 |
}; |
}; |
| 2670 |
|
|
| 2777 |
} |
} |
| 2778 |
} |
} |
| 2779 |
}, |
}, |
| 2780 |
|
## NOTE: param*, then transparent. |
| 2781 |
checker => sub { |
checker => sub { |
| 2782 |
my ($self, $todo) = @_; |
my ($self, $todo) = @_; |
| 2783 |
$todo->{flag}->{has_descendant}->{significant} = 1; |
$todo->{flag}->{has_descendant}->{significant} = 1; |
| 2820 |
my ($self, $todo) = @_; |
my ($self, $todo) = @_; |
| 2821 |
$todo->{flag}->{has_descendant}->{significant} = 1; |
$todo->{flag}->{has_descendant}->{significant} = 1; |
| 2822 |
|
|
| 2823 |
|
## TODO: |
| 2824 |
if ($todo->{node}->has_attribute_ns (undef, 'src')) { |
if ($todo->{node}->has_attribute_ns (undef, 'src')) { |
| 2825 |
return $HTMLBlockOrInlineChecker->($self, $todo); |
return $HTMLBlockOrInlineChecker->($self, $todo); |
| 2826 |
} else { |
} else { |
| 2859 |
checker => sub { |
checker => sub { |
| 2860 |
my ($self, $todo) = @_; |
my ($self, $todo) = @_; |
| 2861 |
$todo->{flag}->{has_descendant}->{significant} = 1; |
$todo->{flag}->{has_descendant}->{significant} = 1; |
| 2862 |
return $HTMLInlineChecker->($self, $todo); |
return $HTMLTransparentChecker->($self, $todo); |
| 2863 |
}, |
}, |
| 2864 |
}; |
}; |
| 2865 |
|
|
| 2893 |
$self->{onerror}->(node => $todo->{node}, type => 'attribute missing:id') |
$self->{onerror}->(node => $todo->{node}, type => 'attribute missing:id') |
| 2894 |
unless $has_id; |
unless $has_id; |
| 2895 |
}, |
}, |
| 2896 |
checker => $HTMLBlockChecker, |
checker => $HTMLProseContentChecker, |
| 2897 |
}; |
}; |
| 2898 |
|
|
| 2899 |
$Element->{$HTML_NS}->{area} = { |
$Element->{$HTML_NS}->{area} = { |
| 3325 |
colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }), |
colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }), |
| 3326 |
rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }), |
rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }), |
| 3327 |
}), |
}), |
| 3328 |
checker => $HTMLBlockOrInlineChecker, |
checker => $HTMLProseContentChecker, |
| 3329 |
}; |
}; |
| 3330 |
|
|
| 3331 |
$Element->{$HTML_NS}->{th} = { |
$Element->{$HTML_NS}->{th} = { |
| 3335 |
scope => $GetHTMLEnumeratedAttrChecker |
scope => $GetHTMLEnumeratedAttrChecker |
| 3336 |
->({row => 1, col => 1, rowgroup => 1, colgroup => 1}), |
->({row => 1, col => 1, rowgroup => 1, colgroup => 1}), |
| 3337 |
}), |
}), |
| 3338 |
checker => $HTMLBlockOrInlineChecker, |
checker => $HTMLProseContentChecker, |
| 3339 |
}; |
}; |
| 3340 |
|
|
| 3341 |
## TODO: forms |
## TODO: forms |
| 3439 |
return ($new_todos); |
return ($new_todos); |
| 3440 |
} else { |
} else { |
| 3441 |
my $end = $self->_add_minuses ({$HTML_NS => {noscript => 1}}); |
my $end = $self->_add_minuses ({$HTML_NS => {noscript => 1}}); |
| 3442 |
my ($sib, $ch) = $HTMLBlockOrInlineChecker->($self, $todo); |
my ($sib, $ch) = $HTMLTransparentChecker->($self, $todo); |
| 3443 |
push @$sib, $end; |
push @$sib, $end; |
| 3444 |
return ($sib, $ch); |
return ($sib, $ch); |
| 3445 |
} |
} |
| 3462 |
checker => sub { |
checker => sub { |
| 3463 |
my ($self, $todo) = @_; |
my ($self, $todo) = @_; |
| 3464 |
|
|
| 3465 |
my $end = $self->_add_minuses ({$HTML_NS => {a => 1, datagrid => 1}}); |
## TODO: |
| 3466 |
my ($sib, $ch) |
my ($sib, $ch) |
| 3467 |
= $GetHTMLZeroOrMoreThenBlockOrInlineChecker->($HTML_NS, 'legend') |
= $GetHTMLZeroOrMoreThenBlockOrInlineChecker->($HTML_NS, 'legend') |
| 3468 |
->($self, $todo); |
->($self, $todo); |
|
push @$sib, $end; |
|
| 3469 |
return ($sib, $ch); |
return ($sib, $ch); |
| 3470 |
}, |
}, |
| 3471 |
}; |
}; |
| 3487 |
|
|
| 3488 |
my $end = $self->_add_minuses ({$HTML_NS => {a => 1, datagrid => 1}}); |
my $end = $self->_add_minuses ({$HTML_NS => {a => 1, datagrid => 1}}); |
| 3489 |
|
|
| 3490 |
## Block-table Block* | table | select | datalist | Empty |
## Prose -(text* table Prose*) | table | select | datalist | Empty |
| 3491 |
my $mode = 'any'; |
my $mode = 'any'; |
| 3492 |
while (@nodes) { |
while (@nodes) { |
| 3493 |
my $node = shift @nodes; |
my $node = shift @nodes; |
| 3501 |
my $not_allowed = $self->{minuses}->{$node_ns}->{$node_ln}; |
my $not_allowed = $self->{minuses}->{$node_ns}->{$node_ln}; |
| 3502 |
if ($self->{pluses}->{$node_ns}->{$node_ln}) { |
if ($self->{pluses}->{$node_ns}->{$node_ln}) { |
| 3503 |
# |
# |
| 3504 |
} elsif ($mode eq 'block') { |
} elsif ($mode eq 'prose') { |
| 3505 |
$not_allowed = 1 |
$not_allowed = 1 |
| 3506 |
unless $HTMLBlockLevelElements->{$node_ns}->{$node_ln}; |
unless $HTMLProseContent->{$node_ns}->{$node_ln}; |
| 3507 |
} elsif ($mode eq 'any') { |
} elsif ($mode eq 'any') { |
| 3508 |
if ($node_ns eq $HTML_NS and |
if ($node_ns eq $HTML_NS and |
| 3509 |
{table => 1, select => 1, datalist => 1}->{$node_ln}) { |
{table => 1, select => 1, datalist => 1}->{$node_ln}) { |
| 3510 |
$mode = 'none'; |
$mode = 'none'; |
| 3511 |
} elsif ($HTMLBlockLevelElements->{$node_ns}->{$node_ln}) { |
} elsif ($HTMLProseContent->{$node_ns}->{$node_ln}) { |
| 3512 |
$mode = 'block'; |
$mode = 'prose'; |
| 3513 |
} else { |
} else { |
| 3514 |
$not_allowed = 1; |
$not_allowed = 1; |
| 3515 |
} |
} |
| 3523 |
push @$new_todos, @$ch; |
push @$new_todos, @$ch; |
| 3524 |
} elsif ($nt == 3 or $nt == 4) { |
} elsif ($nt == 3 or $nt == 4) { |
| 3525 |
if ($node->data =~ /[^\x09-\x0D\x20]/) { |
if ($node->data =~ /[^\x09-\x0D\x20]/) { |
| 3526 |
$self->{onerror}->(node => $node, type => 'character not allowed'); |
if ($mode eq 'prose') { |
| 3527 |
|
# |
| 3528 |
|
} elsif ($mode eq 'any') { |
| 3529 |
|
$mode = 'prose'; |
| 3530 |
|
} else { |
| 3531 |
|
$self->{onerror}->(node => $node, type => 'character not allowed'); |
| 3532 |
|
} |
| 3533 |
$todo->{flag}->{has_descendant}->{significant} = 1; |
$todo->{flag}->{has_descendant}->{significant} = 1; |
| 3534 |
} |
} |
| 3535 |
} elsif ($nt == 5) { |
} elsif ($nt == 5) { |
| 3546 |
}; |
}; |
| 3547 |
|
|
| 3548 |
return ($new_todos); |
return ($new_todos); |
| 3549 |
|
|
| 3550 |
|
## ISSUE: "xxx<table/>" is disallowed; "<select/>aaa" and "<datalist/>aa" |
| 3551 |
|
## are not disallowed (assuming that form control contents are also |
| 3552 |
|
## prose content). |
| 3553 |
}, |
}, |
| 3554 |
}; |
}; |
| 3555 |
|
|
| 3611 |
$todo->{flag}->{has_descendant}->{significant}}; |
$todo->{flag}->{has_descendant}->{significant}}; |
| 3612 |
$todo->{flag}->{has_descendant}->{significant} = 0; |
$todo->{flag}->{has_descendant}->{significant} = 0; |
| 3613 |
|
|
| 3614 |
my $content = 'li or inline'; |
my $content = 'li or phrasing'; |
| 3615 |
while (@nodes) { |
while (@nodes) { |
| 3616 |
my $node = shift @nodes; |
my $node = shift @nodes; |
| 3617 |
$self->_remove_minuses ($node) and next if ref $node eq 'HASH'; |
$self->_remove_minuses ($node) and next if ref $node eq 'HASH'; |
| 3625 |
if ($self->{pluses}->{$node_ns}->{$node_ln}) { |
if ($self->{pluses}->{$node_ns}->{$node_ln}) { |
| 3626 |
# |
# |
| 3627 |
} elsif ($node_ns eq $HTML_NS and $node_ln eq 'li') { |
} elsif ($node_ns eq $HTML_NS and $node_ln eq 'li') { |
| 3628 |
if ($content eq 'inline') { |
if ($content eq 'phrasing') { |
| 3629 |
$not_allowed = 1; |
$not_allowed = 1; |
| 3630 |
} elsif ($content eq 'li or inline') { |
} elsif ($content eq 'li or phrasing') { |
| 3631 |
$content = 'li'; |
$content = 'li'; |
| 3632 |
} |
} |
| 3633 |
} else { |
} else { |
| 3634 |
if ($HTMLStrictlyInlineLevelElements->{$node_ns}->{$node_ln} or |
if ($HTMLPhrasingContent->{$node_ns}->{$node_ln}) { |
| 3635 |
$HTMLStructuredInlineLevelElements->{$node_ns}->{$node_ln}) { |
$content = 'phrasing'; |
|
$content = 'inline'; |
|
| 3636 |
} else { |
} else { |
| 3637 |
$not_allowed = 1; |
$not_allowed = 1; |
| 3638 |
} |
} |
| 3646 |
if ($node->data =~ /[^\x09-\x0D\x20]/) { |
if ($node->data =~ /[^\x09-\x0D\x20]/) { |
| 3647 |
if ($content eq 'li') { |
if ($content eq 'li') { |
| 3648 |
$self->{onerror}->(node => $node, type => 'character not allowed'); |
$self->{onerror}->(node => $node, type => 'character not allowed'); |
| 3649 |
} elsif ($content eq 'li or inline') { |
} elsif ($content eq 'li or phrasing') { |
| 3650 |
$content = 'inline'; |
$content = 'phrasing'; |
| 3651 |
} |
} |
| 3652 |
$todo->{flag}->{has_descendant}->{significant} = 1; |
$todo->{flag}->{has_descendant}->{significant} = 1; |
| 3653 |
} |
} |
| 3657 |
} |
} |
| 3658 |
|
|
| 3659 |
for (@$new_todos) { |
for (@$new_todos) { |
| 3660 |
$_->{inline} = 1; |
$_->{flag}->{in_menu} = 1; |
| 3661 |
} |
} |
| 3662 |
|
|
| 3663 |
push @$new_todos, { |
push @$new_todos, { |
| 3745 |
|
|
| 3746 |
$Element->{$HTML_NS}->{legend} = { |
$Element->{$HTML_NS}->{legend} = { |
| 3747 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 3748 |
checker => sub { |
checker => $HTMLPhrasingContentChecker, |
|
my ($self, $todo) = @_; |
|
|
|
|
|
my $parent = $todo->{node}->manakai_parent_element; |
|
|
if (defined $parent) { |
|
|
my $nsuri = $parent->namespace_uri; |
|
|
$nsuri = '' unless defined $nsuri; |
|
|
my $ln = $parent->manakai_local_name; |
|
|
if ($nsuri eq $HTML_NS and $ln eq 'figure') { |
|
|
return $HTMLInlineChecker->($self, $todo); |
|
|
} else { |
|
|
return $HTMLStrictlyInlineChecker->($self, $todo); |
|
|
} |
|
|
} else { |
|
|
return $HTMLInlineChecker->($self, $todo); |
|
|
} |
|
|
|
|
|
## ISSUE: Content model is defined only for fieldset/legend, |
|
|
## details/legend, and figure/legend. |
|
|
}, |
|
| 3749 |
}; |
}; |
| 3750 |
|
|
| 3751 |
$Element->{$HTML_NS}->{div} = { |
$Element->{$HTML_NS}->{div} = { |
| 3752 |
attrs_checker => $GetHTMLAttrsChecker->({}), |
attrs_checker => $GetHTMLAttrsChecker->({}), |
| 3753 |
checker => $GetHTMLZeroOrMoreThenBlockOrInlineChecker->($HTML_NS, 'style'), |
checker => $HTMLTransparentChecker, |
| 3754 |
}; |
}; |
| 3755 |
|
|
| 3756 |
$Element->{$HTML_NS}->{font} = { |
$Element->{$HTML_NS}->{font} = { |