| 505 |
## |rel| attribute (unordered set of space separated tokens, |
## |rel| attribute (unordered set of space separated tokens, |
| 506 |
## whose allowed values are defined by the section on link types) |
## whose allowed values are defined by the section on link types) |
| 507 |
my $HTMLLinkTypesAttrChecker = sub { |
my $HTMLLinkTypesAttrChecker = sub { |
| 508 |
my ($a_or_area, $self, $attr) = @_; |
my ($a_or_area, $todo, $self, $attr) = @_; |
| 509 |
my %word; |
my %word; |
| 510 |
for my $word (grep {length $_} split /[\x09-\x0D\x20]/, $attr->value) { |
for my $word (grep {length $_} split /[\x09-\x0D\x20]/, $attr->value) { |
| 511 |
unless ($word{$word}) { |
unless ($word{$word}) { |
| 540 |
$self->{onerror}->(node => $attr, |
$self->{onerror}->(node => $attr, |
| 541 |
type => 'link type:non-conforming:'.$word); |
type => 'link type:non-conforming:'.$word); |
| 542 |
} |
} |
| 543 |
|
if (defined $def->{effect}->[$a_or_area]) { |
| 544 |
|
if ($word eq 'alternate') { |
| 545 |
|
# |
| 546 |
|
} elsif ($def->{effect}->[$a_or_area] eq 'hyperlink') { |
| 547 |
|
$todo->{has_hyperlink_link_type} = 1; |
| 548 |
|
} |
| 549 |
|
} |
| 550 |
if ($def->{unique}) { |
if ($def->{unique}) { |
| 551 |
unless ($self->{has_link_type}->{$word}) { |
unless ($self->{has_link_type}->{$word}) { |
| 552 |
$self->{has_link_type}->{$word} = 1; |
$self->{has_link_type}->{$word} = 1; |
| 560 |
type => 'link type:'.$word); |
type => 'link type:'.$word); |
| 561 |
} |
} |
| 562 |
} |
} |
| 563 |
|
$todo->{has_hyperlink_link_type} = 1 |
| 564 |
|
if $word{alternate} and not $word{stylesheet}; |
| 565 |
## TODO: The Pingback 1.0 specification, which is referenced by HTML5, |
## TODO: The Pingback 1.0 specification, which is referenced by HTML5, |
| 566 |
## says that using both X-Pingback: header field and HTML |
## says that using both X-Pingback: header field and HTML |
| 567 |
## <link rel=pingback> is deprecated and if both appears they |
## <link rel=pingback> is deprecated and if both appears they |
| 585 |
type => 'URI::'.$opt{type}. |
type => 'URI::'.$opt{type}. |
| 586 |
(defined $opt{position} ? ':'.$opt{position} : '')); |
(defined $opt{position} ? ':'.$opt{position} : '')); |
| 587 |
}); |
}); |
| 588 |
|
$self->{has_uri_attr} = 1; |
| 589 |
}; # $HTMLURIAttrChecker |
}; # $HTMLURIAttrChecker |
| 590 |
|
|
| 591 |
## A space separated list of one or more URIs (or IRIs) |
## A space separated list of one or more URIs (or IRIs) |
| 607 |
## ISSUE: A sequence of white space characters are conformant? |
## ISSUE: A sequence of white space characters are conformant? |
| 608 |
## ISSUE: A zero-length string is conformant? (It does contain a relative reference, i.e. same as base URI.) |
## ISSUE: A zero-length string is conformant? (It does contain a relative reference, i.e. same as base URI.) |
| 609 |
## NOTE: Duplication seems not an error. |
## NOTE: Duplication seems not an error. |
| 610 |
|
$self->{has_uri_attr} = 1; |
| 611 |
}; # $HTMLSpaceURIsAttrChecker |
}; # $HTMLSpaceURIsAttrChecker |
| 612 |
|
|
| 613 |
my $HTMLDatetimeAttrChecker = sub { |
my $HTMLDatetimeAttrChecker = sub { |
| 1039 |
checker => $HTMLTextChecker, |
checker => $HTMLTextChecker, |
| 1040 |
}; |
}; |
| 1041 |
|
|
|
## TODO: |base| with |href| MUST come before elements with URI attributes. |
|
|
## For example: <title xml:base=""/><base href=""/> is non-conformant. |
|
|
## TODO: |base| with |target| MUST come before any hyperlink. |
|
| 1042 |
$Element->{$HTML_NS}->{base} = { |
$Element->{$HTML_NS}->{base} = { |
| 1043 |
attrs_checker => $GetHTMLAttrsChecker->({ |
attrs_checker => sub { |
| 1044 |
href => $HTMLURIAttrChecker, |
my ($self, $todo) = @_; |
| 1045 |
target => $HTMLTargetAttrChecker, |
|
| 1046 |
}), |
if ($self->{has_uri_attr} and |
| 1047 |
|
$todo->{node}->has_attribute_ns (undef, 'href')) { |
| 1048 |
|
## ISSUE: Are these examples conforming? |
| 1049 |
|
## <head profile="a b c"><base href> (except for |profile|'s |
| 1050 |
|
## non-conformance) |
| 1051 |
|
## <title xml:base="relative"/><base href/> (maybe it should be) |
| 1052 |
|
## <unknown xmlns="relative"/><base href/> (assuming that |
| 1053 |
|
## |{relative}:unknown| is allowed before XHTML |base| (unlikely, though)) |
| 1054 |
|
## <?xml-stylesheet href="relative"?>...<base href=""/> |
| 1055 |
|
## NOTE: These are non-conformant anyway because of |head|'s content model: |
| 1056 |
|
## <style>@import 'relative';</style><base href> |
| 1057 |
|
## <script>location.href = 'relative';</script><base href> |
| 1058 |
|
$self->{onerror}->(node => $todo->{node}, |
| 1059 |
|
type => 'basehref after URI attribute'); |
| 1060 |
|
} |
| 1061 |
|
if ($self->{has_hyperlink_element} and |
| 1062 |
|
$todo->{node}->has_attribute_ns (undef, 'target')) { |
| 1063 |
|
## ISSUE: Are these examples conforming? |
| 1064 |
|
## <head><title xlink:href=""/><base target="name"/></head> |
| 1065 |
|
## <xbl:xbl>...<svg:a href=""/>...</xbl:xbl><base target="name"/> |
| 1066 |
|
## (assuming that |xbl:xbl| is allowed before |base|) |
| 1067 |
|
## NOTE: These are non-conformant anyway because of |head|'s content model: |
| 1068 |
|
## <link href=""/><base target="name"/> |
| 1069 |
|
## <link rel=unknown href=""><base target=name> |
| 1070 |
|
$self->{onerror}->(node => $todo->{node}, |
| 1071 |
|
type => 'basetarget after hyperlink'); |
| 1072 |
|
} |
| 1073 |
|
|
| 1074 |
|
return $GetHTMLAttrsChecker->({ |
| 1075 |
|
href => $HTMLURIAttrChecker, |
| 1076 |
|
target => $HTMLTargetAttrChecker, |
| 1077 |
|
})->($self, $todo); |
| 1078 |
|
}, |
| 1079 |
checker => $HTMLEmptyChecker, |
checker => $HTMLEmptyChecker, |
| 1080 |
}; |
}; |
| 1081 |
|
|
| 1084 |
my ($self, $todo) = @_; |
my ($self, $todo) = @_; |
| 1085 |
$GetHTMLAttrsChecker->({ |
$GetHTMLAttrsChecker->({ |
| 1086 |
href => $HTMLURIAttrChecker, |
href => $HTMLURIAttrChecker, |
| 1087 |
rel => sub { $HTMLLinkTypesAttrChecker->(0, @_) }, |
rel => sub { $HTMLLinkTypesAttrChecker->(0, $todo, @_) }, |
| 1088 |
media => $HTMLMQAttrChecker, |
media => $HTMLMQAttrChecker, |
| 1089 |
hreflang => $HTMLLanguageTagAttrChecker, |
hreflang => $HTMLLanguageTagAttrChecker, |
| 1090 |
type => $HTMLIMTAttrChecker, |
type => $HTMLIMTAttrChecker, |
| 1091 |
## NOTE: Though |title| has special semantics, |
## NOTE: Though |title| has special semantics, |
| 1092 |
## syntactically same as the |title| as global attribute. |
## syntactically same as the |title| as global attribute. |
| 1093 |
})->($self, $todo); |
})->($self, $todo); |
| 1094 |
unless ($todo->{node}->has_attribute_ns (undef, 'href')) { |
if ($todo->{node}->has_attribute_ns (undef, 'href')) { |
| 1095 |
|
$self->{has_hyperlink_element} = 1 if $todo->{has_hyperlink_link_type}; |
| 1096 |
|
} else { |
| 1097 |
$self->{onerror}->(node => $todo->{node}, |
$self->{onerror}->(node => $todo->{node}, |
| 1098 |
type => 'attribute missing:href'); |
type => 'attribute missing:href'); |
| 1099 |
} |
} |
| 1671 |
target => $HTMLTargetAttrChecker, |
target => $HTMLTargetAttrChecker, |
| 1672 |
href => $HTMLURIAttrChecker, |
href => $HTMLURIAttrChecker, |
| 1673 |
ping => $HTMLSpaceURIsAttrChecker, |
ping => $HTMLSpaceURIsAttrChecker, |
| 1674 |
rel => sub { $HTMLLinkTypesAttrChecker->(1, @_) }, |
rel => sub { $HTMLLinkTypesAttrChecker->(1, $todo, @_) }, |
| 1675 |
media => $HTMLMQAttrChecker, |
media => $HTMLMQAttrChecker, |
| 1676 |
hreflang => $HTMLLanguageTagAttrChecker, |
hreflang => $HTMLLanguageTagAttrChecker, |
| 1677 |
type => $HTMLIMTAttrChecker, |
type => $HTMLIMTAttrChecker, |
| 1693 |
} |
} |
| 1694 |
} |
} |
| 1695 |
|
|
| 1696 |
unless (defined $attr{href}) { |
if (defined $attr{href}) { |
| 1697 |
|
$self->{has_hyperlink_element} = 1; |
| 1698 |
|
} else { |
| 1699 |
for (qw/target ping rel media hreflang type/) { |
for (qw/target ping rel media hreflang type/) { |
| 1700 |
if (defined $attr{$_}) { |
if (defined $attr{$_}) { |
| 1701 |
$self->{onerror}->(node => $attr{$_}, |
$self->{onerror}->(node => $attr{$_}, |
| 2063 |
|
|
| 2064 |
## TODO: figure |
## TODO: figure |
| 2065 |
|
|
| 2066 |
|
## TODO: |alt| |
| 2067 |
$Element->{$HTML_NS}->{img} = { |
$Element->{$HTML_NS}->{img} = { |
| 2068 |
attrs_checker => sub { |
attrs_checker => sub { |
| 2069 |
my ($self, $todo) = @_; |
my ($self, $todo) = @_; |
| 2229 |
}; |
}; |
| 2230 |
|
|
| 2231 |
$Element->{$HTML_NS}->{map} = { |
$Element->{$HTML_NS}->{map} = { |
| 2232 |
attrs_checker => $GetHTMLAttrsChecker->({ |
attrs_checker => sub { |
| 2233 |
id => sub { |
my ($self, $todo) = @_; |
| 2234 |
## NOTE: same as global |id=""|, with |$self->{map}| registeration |
my $has_id; |
| 2235 |
my ($self, $attr) = @_; |
$GetHTMLAttrsChecker->({ |
| 2236 |
my $value = $attr->value; |
id => sub { |
| 2237 |
if (length $value > 0) { |
## NOTE: same as global |id=""|, with |$self->{map}| registeration |
| 2238 |
if ($self->{id}->{$value}) { |
my ($self, $attr) = @_; |
| 2239 |
$self->{onerror}->(node => $attr, type => 'duplicate ID'); |
my $value = $attr->value; |
| 2240 |
push @{$self->{id}->{$value}}, $attr; |
if (length $value > 0) { |
| 2241 |
|
if ($self->{id}->{$value}) { |
| 2242 |
|
$self->{onerror}->(node => $attr, type => 'duplicate ID'); |
| 2243 |
|
push @{$self->{id}->{$value}}, $attr; |
| 2244 |
|
} else { |
| 2245 |
|
$self->{id}->{$value} = [$attr]; |
| 2246 |
|
} |
| 2247 |
} else { |
} else { |
| 2248 |
$self->{id}->{$value} = [$attr]; |
## NOTE: MUST contain at least one character |
| 2249 |
|
$self->{onerror}->(node => $attr, type => 'empty attribute value'); |
| 2250 |
} |
} |
| 2251 |
} else { |
if ($value =~ /[\x09-\x0D\x20]/) { |
| 2252 |
## NOTE: MUST contain at least one character |
$self->{onerror}->(node => $attr, type => 'space in ID'); |
| 2253 |
$self->{onerror}->(node => $attr, type => 'empty attribute value'); |
} |
| 2254 |
} |
$self->{map}->{$value} ||= $attr; |
| 2255 |
if ($value =~ /[\x09-\x0D\x20]/) { |
$has_id = 1; |
| 2256 |
$self->{onerror}->(node => $attr, type => 'space in ID'); |
}, |
| 2257 |
} |
})->($self, $todo); |
| 2258 |
$self->{map}->{$value} ||= $attr; |
$self->{onerror}->(node => $todo->{node}, type => 'attribute missing:id') |
| 2259 |
}, |
unless $has_id; |
| 2260 |
}), |
}, |
| 2261 |
checker => $HTMLBlockChecker, |
checker => $HTMLBlockChecker, |
|
## TODO: |id| is required. |
|
| 2262 |
}; |
}; |
| 2263 |
|
|
| 2264 |
$Element->{$HTML_NS}->{area} = { |
$Element->{$HTML_NS}->{area} = { |
| 2294 |
target => $HTMLTargetAttrChecker, |
target => $HTMLTargetAttrChecker, |
| 2295 |
href => $HTMLURIAttrChecker, |
href => $HTMLURIAttrChecker, |
| 2296 |
ping => $HTMLSpaceURIsAttrChecker, |
ping => $HTMLSpaceURIsAttrChecker, |
| 2297 |
rel => sub { $HTMLLinkTypesAttrChecker->(1, @_) }, |
rel => sub { $HTMLLinkTypesAttrChecker->(1, $todo, @_) }, |
| 2298 |
media => $HTMLMQAttrChecker, |
media => $HTMLMQAttrChecker, |
| 2299 |
hreflang => $HTMLLanguageTagAttrChecker, |
hreflang => $HTMLLanguageTagAttrChecker, |
| 2300 |
type => $HTMLIMTAttrChecker, |
type => $HTMLIMTAttrChecker, |
| 2317 |
} |
} |
| 2318 |
|
|
| 2319 |
if (defined $attr{href}) { |
if (defined $attr{href}) { |
| 2320 |
|
$self->{has_hyperlink_element} = 1; |
| 2321 |
unless (defined $attr{alt}) { |
unless (defined $attr{alt}) { |
| 2322 |
$self->{onerror}->(node => $todo->{node}, |
$self->{onerror}->(node => $todo->{node}, |
| 2323 |
type => 'attribute missing:alt'); |
type => 'attribute missing:alt'); |