957 |
}, |
}, |
958 |
dir => $GetHTMLEnumeratedAttrChecker->({ltr => 1, rtl => 1}), |
dir => $GetHTMLEnumeratedAttrChecker->({ltr => 1, rtl => 1}), |
959 |
class => $HTMLUnorderedSetOfSpaceSeparatedTokensAttrChecker, |
class => $HTMLUnorderedSetOfSpaceSeparatedTokensAttrChecker, |
960 |
|
contextmenu => sub { |
961 |
|
my ($self, $attr) = @_; |
962 |
|
my $value = $attr->value; |
963 |
|
push @{$self->{contextmenu}}, [$value => $attr]; |
964 |
|
## ISSUE: "The value must be the ID of a menu element in the DOM." |
965 |
|
## What is "in the DOM"? A menu Element node that is not part |
966 |
|
## of the Document tree is in the DOM? A menu Element node that |
967 |
|
## belong to another Document tree is in the DOM? |
968 |
|
}, |
969 |
irrelevant => $GetHTMLBooleanAttrChecker->('irrelevant'), |
irrelevant => $GetHTMLBooleanAttrChecker->('irrelevant'), |
970 |
tabindex => $HTMLIntegerAttrChecker, |
tabindex => $HTMLIntegerAttrChecker, |
971 |
}; |
}; |
997 |
$checker ||= $AttrChecker->{$attr_ns}->{$attr_ln} |
$checker ||= $AttrChecker->{$attr_ns}->{$attr_ln} |
998 |
|| $AttrChecker->{$attr_ns}->{''}; |
|| $AttrChecker->{$attr_ns}->{''}; |
999 |
if ($checker) { |
if ($checker) { |
1000 |
$checker->($self, $attr); |
$checker->($self, $attr, $todo); |
1001 |
} else { |
} else { |
1002 |
$self->{onerror}->(node => $attr, type => 'attribute not supported'); |
$self->{onerror}->(node => $attr, type => 'attribute not supported'); |
1003 |
## ISSUE: No comformance createria for unknown attributes in the spec |
## ISSUE: No comformance createria for unknown attributes in the spec |
1783 |
my ($self, $todo) = @_; |
my ($self, $todo) = @_; |
1784 |
|
|
1785 |
my $end = $self->_add_minuses ($HTMLInteractiveElements); |
my $end = $self->_add_minuses ($HTMLInteractiveElements); |
1786 |
my ($sib, $ch) |
my ($new_todos, $ch) |
1787 |
= $HTMLSignificantInlineOrStrictlyInlineChecker->($self, $todo); |
= $HTMLSignificantInlineOrStrictlyInlineChecker->($self, $todo); |
1788 |
push @$sib, $end; |
push @$new_todos, $end; |
1789 |
return ($sib, $ch); |
|
1790 |
|
$_->{flag}->{has_a} = 1 for @$new_todos; |
1791 |
|
|
1792 |
|
return ($new_todos, $ch); |
1793 |
}, |
}, |
1794 |
}; |
}; |
1795 |
|
|
2016 |
alt => sub { }, ## NOTE: No syntactical requirement |
alt => sub { }, ## NOTE: No syntactical requirement |
2017 |
src => $HTMLURIAttrChecker, |
src => $HTMLURIAttrChecker, |
2018 |
usemap => $HTMLUsemapAttrChecker, |
usemap => $HTMLUsemapAttrChecker, |
2019 |
ismap => $GetHTMLBooleanAttrChecker->('ismap'), ## TODO: MUST ancestor <a> |
ismap => sub { |
2020 |
|
my ($self, $attr, $parent_todo) = @_; |
2021 |
|
if (not $todo->{flag}->{has_a}) { |
2022 |
|
$self->{onerror}->(node => $attr, type => 'attribute not allowed'); |
2023 |
|
} |
2024 |
|
$GetHTMLBooleanAttrChecker->('ismap')->($self, $attr, $parent_todo); |
2025 |
|
}, |
2026 |
## TODO: height |
## TODO: height |
2027 |
## TODO: width |
## TODO: width |
2028 |
})->($self, $todo); |
})->($self, $todo); |
2698 |
}), |
}), |
2699 |
checker => sub { |
checker => sub { |
2700 |
my ($self, $todo) = @_; |
my ($self, $todo) = @_; |
2701 |
|
my $el = $todo->{node}; |
2702 |
|
my $new_todos = []; |
2703 |
|
my @nodes = (@{$el->child_nodes}); |
2704 |
|
|
2705 |
my $end = $self->_add_minuses ({$HTML_NS => {a => 1, datagrid => 1}}); |
my $end = $self->_add_minuses ({$HTML_NS => {a => 1, datagrid => 1}}); |
2706 |
my ($sib, $ch) = $HTMLBlockChecker->($self, $todo); |
|
2707 |
## TODO: (Block-table)+ | table | select | datalist |
## Block-table Block* | table | select | datalist | Empty |
2708 |
push @$sib, $end; |
my $mode = 'any'; |
2709 |
return ($sib, $ch); |
while (@nodes) { |
2710 |
|
my $node = shift @nodes; |
2711 |
|
$self->_remove_minuses ($node) and next if ref $node eq 'HASH'; |
2712 |
|
|
2713 |
|
my $nt = $node->node_type; |
2714 |
|
if ($nt == 1) { |
2715 |
|
my $node_ns = $node->namespace_uri; |
2716 |
|
$node_ns = '' unless defined $node_ns; |
2717 |
|
my $node_ln = $node->manakai_local_name; |
2718 |
|
my $not_allowed = $self->{minuses}->{$node_ns}->{$node_ln}; |
2719 |
|
if ($mode eq 'block') { |
2720 |
|
$not_allowed = 1 |
2721 |
|
unless $HTMLBlockLevelElements->{$node_ns}->{$node_ln}; |
2722 |
|
} elsif ($mode eq 'any') { |
2723 |
|
if ($node_ns eq $HTML_NS and |
2724 |
|
{table => 1, select => 1, datalist => 1}->{$node_ln}) { |
2725 |
|
$mode = 'none'; |
2726 |
|
} elsif ($HTMLBlockLevelElements->{$node_ns}->{$node_ln}) { |
2727 |
|
$mode = 'block'; |
2728 |
|
} else { |
2729 |
|
$not_allowed = 1; |
2730 |
|
} |
2731 |
|
} else { |
2732 |
|
$not_allowed = 1; |
2733 |
|
} |
2734 |
|
$self->{onerror}->(node => $node, type => 'element not allowed') |
2735 |
|
if $not_allowed; |
2736 |
|
my ($sib, $ch) = $self->_check_get_children ($node, $todo); |
2737 |
|
unshift @nodes, @$sib; |
2738 |
|
push @$new_todos, @$ch; |
2739 |
|
} elsif ($nt == 3 or $nt == 4) { |
2740 |
|
if ($node->data =~ /[^\x09-\x0D\x20]/) { |
2741 |
|
$self->{onerror}->(node => $node, type => 'character not allowed'); |
2742 |
|
} |
2743 |
|
} elsif ($nt == 5) { |
2744 |
|
unshift @nodes, @{$node->child_nodes}; |
2745 |
|
} |
2746 |
|
} |
2747 |
|
|
2748 |
|
push @$new_todos, $end; |
2749 |
|
return ($new_todos); |
2750 |
}, |
}, |
2751 |
}; |
}; |
2752 |
|
|
2753 |
$Element->{$HTML_NS}->{command} = { |
$Element->{$HTML_NS}->{command} = { |
2754 |
attrs_checker => $GetHTMLAttrsChecker->({}), ## TODO |
attrs_checker => $GetHTMLAttrsChecker->({ |
2755 |
|
checked => $GetHTMLBooleanAttrChecker->('checked'), |
2756 |
|
default => $GetHTMLBooleanAttrChecker->('default'), |
2757 |
|
disabled => $GetHTMLBooleanAttrChecker->('disabled'), |
2758 |
|
hidden => $GetHTMLBooleanAttrChecker->('hidden'), |
2759 |
|
icon => $HTMLURIAttrChecker, |
2760 |
|
label => sub { }, ## NOTE: No conformance creteria |
2761 |
|
radiogroup => sub { }, ## NOTE: No conformance creteria |
2762 |
|
## NOTE: |title| has special semantics, but no syntactical difference |
2763 |
|
type => sub { |
2764 |
|
my ($self, $attr) = @_; |
2765 |
|
my $value = $attr->value; |
2766 |
|
unless ({command => 1, checkbox => 1, radio => 1}->{$value}) { |
2767 |
|
$self->{onerror}->(node => $attr, type => 'attribute value not allowed'); |
2768 |
|
} |
2769 |
|
}, |
2770 |
|
}), |
2771 |
checker => $HTMLEmptyChecker, |
checker => $HTMLEmptyChecker, |
2772 |
}; |
}; |
2773 |
|
|
2774 |
$Element->{$HTML_NS}->{menu} = { |
$Element->{$HTML_NS}->{menu} = { |
2775 |
attrs_checker => $GetHTMLAttrsChecker->({}), ## TODO |
attrs_checker => $GetHTMLAttrsChecker->({ |
2776 |
|
autosubmit => $GetHTMLBooleanAttrChecker->('autosubmit'), |
2777 |
|
id => sub { |
2778 |
|
## NOTE: same as global |id=""|, with |$self->{menu}| registeration |
2779 |
|
my ($self, $attr) = @_; |
2780 |
|
my $value = $attr->value; |
2781 |
|
if (length $value > 0) { |
2782 |
|
if ($self->{id}->{$value}) { |
2783 |
|
$self->{onerror}->(node => $attr, type => 'duplicate ID'); |
2784 |
|
} else { |
2785 |
|
$self->{id}->{$value} = 1; |
2786 |
|
} |
2787 |
|
} else { |
2788 |
|
## NOTE: MUST contain at least one character |
2789 |
|
$self->{onerror}->(node => $attr, type => 'attribute value is empty'); |
2790 |
|
} |
2791 |
|
if ($value =~ /[\x09-\x0D\x20]/) { |
2792 |
|
$self->{onerror}->(node => $attr, type => 'space in ID'); |
2793 |
|
} |
2794 |
|
$self->{menu}->{$value} ||= $attr; |
2795 |
|
## ISSUE: <menu id=""><p contextmenu=""> match? |
2796 |
|
}, |
2797 |
|
label => sub { }, ## NOTE: No conformance creteria |
2798 |
|
type => $GetHTMLEnumeratedAttrChecker->({context => 1, toolbar => 1}), |
2799 |
|
}), |
2800 |
checker => sub { |
checker => sub { |
2801 |
my ($self, $todo) = @_; |
my ($self, $todo) = @_; |
2802 |
my $el = $todo->{node}; |
my $el = $todo->{node}; |
2928 |
$self->{id} = {}; |
$self->{id} = {}; |
2929 |
$self->{term} = {}; |
$self->{term} = {}; |
2930 |
$self->{usemap} = []; |
$self->{usemap} = []; |
2931 |
|
$self->{contextmenu} = []; |
2932 |
$self->{map} = {}; |
$self->{map} = {}; |
2933 |
|
$self->{menu} = {}; |
2934 |
$self->{has_link_type} = {}; |
$self->{has_link_type} = {}; |
2935 |
|
|
2936 |
my @todo = ({type => 'element', node => $el}); |
my @todo = ({type => 'element', node => $el}); |
2981 |
} |
} |
2982 |
} |
} |
2983 |
|
|
2984 |
|
for (@{$self->{contextmenu}}) { |
2985 |
|
unless ($self->{menu}->{$_->[0]}) { |
2986 |
|
$self->{onerror}->(node => $_->[1], type => 'no referenced menu'); |
2987 |
|
} |
2988 |
|
} |
2989 |
|
|
2990 |
delete $self->{minuses}; |
delete $self->{minuses}; |
2991 |
delete $self->{onerror}; |
delete $self->{onerror}; |
2992 |
delete $self->{id}; |
delete $self->{id}; |