| 1 |
package Whatpm::ContentChecker; |
| 2 |
use strict; |
| 3 |
require Whatpm::ContentChecker; |
| 4 |
|
| 5 |
use Char::Class::XML qw/InXML_NCNameStartChar10 InXMLNCNameChar10/; |
| 6 |
|
| 7 |
my $HTML_NS = q<http://www.w3.org/1999/xhtml>; |
| 8 |
|
| 9 |
sub FEATURE_HTML5_ROLE () { |
| 10 |
Whatpm::ContentChecker::FEATURE_STATUS_WD |
| 11 |
## TODO: svg:*/@role |
| 12 |
} |
| 13 |
|
| 14 |
sub FEATURE_HTML5_COMPLETE () { |
| 15 |
Whatpm::ContentChecker::FEATURE_STATUS_REC | |
| 16 |
Whatpm::ContentChecker::FEATURE_ALLOWED |
| 17 |
} |
| 18 |
|
| 19 |
sub FEATURE_HTML5_LC () { |
| 20 |
Whatpm::ContentChecker::FEATURE_STATUS_LC | |
| 21 |
Whatpm::ContentChecker::FEATURE_ALLOWED |
| 22 |
} |
| 23 |
sub FEATURE_HTML5_AT_RISK () { |
| 24 |
Whatpm::ContentChecker::FEATURE_STATUS_WD | |
| 25 |
Whatpm::ContentChecker::FEATURE_ALLOWED |
| 26 |
} |
| 27 |
sub FEATURE_HTML5_WD () { |
| 28 |
Whatpm::ContentChecker::FEATURE_STATUS_WD | |
| 29 |
Whatpm::ContentChecker::FEATURE_ALLOWED |
| 30 |
} |
| 31 |
sub FEATURE_HTML5_FD () { |
| 32 |
Whatpm::ContentChecker::FEATURE_STATUS_WD | |
| 33 |
Whatpm::ContentChecker::FEATURE_ALLOWED |
| 34 |
} |
| 35 |
sub FEATURE_HTML5_DEFAULT () { |
| 36 |
Whatpm::ContentChecker::FEATURE_STATUS_WD | |
| 37 |
Whatpm::ContentChecker::FEATURE_ALLOWED |
| 38 |
} |
| 39 |
sub FEATURE_HTML5_DROPPED () { |
| 40 |
## NOTE: Was part of HTML5, but was dropped. |
| 41 |
Whatpm::ContentChecker::FEATURE_STATUS_WD |
| 42 |
} |
| 43 |
sub FEATURE_HTML5_LC_DROPPED () { |
| 44 |
## NOTE: Was part of HTML5, but was dropped. |
| 45 |
Whatpm::ContentChecker::FEATURE_STATUS_LC |
| 46 |
} |
| 47 |
sub FEATURE_WF2X () { |
| 48 |
## NOTE: Defined in WF2 and then incorporated into the HTML5 spec. |
| 49 |
Whatpm::ContentChecker::FEATURE_STATUS_LC |
| 50 |
} |
| 51 |
sub FEATURE_WF2 () { |
| 52 |
## NOTE: Part of WF2, still not added to the HTML5 spec. |
| 53 |
Whatpm::ContentChecker::FEATURE_STATUS_LC |
| 54 |
} |
| 55 |
sub FEATURE_WF2_INFORMATIVE () { |
| 56 |
## NOTE: Part of WF2 Appendix A, still not added to the HTML5 spec. |
| 57 |
Whatpm::ContentChecker::FEATURE_STATUS_LC |
| 58 |
} |
| 59 |
sub FEATURE_WF2X_DEPRECATED () { |
| 60 |
Whatpm::ContentChecker::FEATURE_STATUS_LC |
| 61 |
## NOTE: MUST NOT be used according to WF2. |
| 62 |
} |
| 63 |
|
| 64 |
## NOTE: Metainformation Attributes Module by W3C XHTML2 WG. |
| 65 |
sub FEATURE_RDFA_PR () { |
| 66 |
Whatpm::ContentChecker::FEATURE_STATUS_CR |
| 67 |
## NOTE: Diff from FEATURE_RDFA_CR |
| 68 |
} |
| 69 |
sub FEATURE_RDFA_CR () { |
| 70 |
Whatpm::ContentChecker::FEATURE_STATUS_CR |
| 71 |
} |
| 72 |
sub FEATURE_RDFA_LC () { |
| 73 |
Whatpm::ContentChecker::FEATURE_STATUS_LC |
| 74 |
} |
| 75 |
|
| 76 |
sub FEATURE_RDFA_ED () { |
| 77 |
Whatpm::ContentChecker::FEATURE_STATUS_WD |
| 78 |
} |
| 79 |
|
| 80 |
## NOTE: XHTML Role LCWD has almost no information on how the |role| |
| 81 |
## attribute can be used- the only requirements for that matter is: |
| 82 |
## "the attribute MUST be referenced using its namespace-qualified form" (and |
| 83 |
## this is a host language conformance!). |
| 84 |
sub FEATURE_ROLE_LC () { |
| 85 |
Whatpm::ContentChecker::FEATURE_STATUS_LC |
| 86 |
} |
| 87 |
|
| 88 |
sub FEATURE_XHTML2_ED () { |
| 89 |
Whatpm::ContentChecker::FEATURE_STATUS_WD |
| 90 |
} |
| 91 |
|
| 92 |
sub FEATURE_XHTMLBASIC11_CR () { |
| 93 |
## NOTE: Only additions to M12N10_REC are marked. |
| 94 |
Whatpm::ContentChecker::FEATURE_STATUS_CR |
| 95 |
} |
| 96 |
sub FEATURE_XHTMLBASIC11_CR_DEPRECATED () { |
| 97 |
Whatpm::ContentChecker::FEATURE_STATUS_CR | |
| 98 |
Whatpm::ContentChecker::FEATURE_DEPRECATED_INFO |
| 99 |
} |
| 100 |
|
| 101 |
sub FEATURE_M12N11_LC () { |
| 102 |
Whatpm::ContentChecker::FEATURE_STATUS_LC |
| 103 |
} |
| 104 |
|
| 105 |
sub FEATURE_RUBY_REC () { |
| 106 |
Whatpm::ContentChecker::FEATURE_STATUS_CR |
| 107 |
} |
| 108 |
|
| 109 |
## NOTE: M12N10 status is based on its abstract module definition. |
| 110 |
## It contains a number of problems. (However, again, it's a REC!) |
| 111 |
sub FEATURE_M12N10_REC () { |
| 112 |
## NOTE: Oh, XHTML m12n 1.0 passed the CR phase! W3C Process suck! |
| 113 |
Whatpm::ContentChecker::FEATURE_STATUS_REC |
| 114 |
} |
| 115 |
sub FEATURE_M12N10_REC_DEPRECATED () { |
| 116 |
Whatpm::ContentChecker::FEATURE_STATUS_REC | |
| 117 |
Whatpm::ContentChecker::FEATURE_DEPRECATED_INFO |
| 118 |
} |
| 119 |
## NOTE: XHTML M12N 1.1 is in the PR status at the time of writing and no |
| 120 |
## addition from 1.0. |
| 121 |
|
| 122 |
## NOTE: XHTML10 status is based on its transitional and frameset DTDs |
| 123 |
## (second edition). Only missing attributes from M12N10 abstract |
| 124 |
## definition are added. |
| 125 |
sub FEATURE_XHTML10_REC () { |
| 126 |
Whatpm::ContentChecker::FEATURE_STATUS_CR |
| 127 |
} |
| 128 |
|
| 129 |
## NOTE: Diff from HTML4. |
| 130 |
sub FEATURE_ISOHTML_PREPARATION () { ## Informative documentation |
| 131 |
Whatpm::ContentChecker::FEATURE_STATUS_CR |
| 132 |
} |
| 133 |
|
| 134 |
## NOTE: HTML4 status is based on its transitional and frameset DTDs (HTML |
| 135 |
## 4.01). Only missing attributes from XHTML10 are added. |
| 136 |
sub FEATURE_HTML4_REC_RESERVED () { |
| 137 |
Whatpm::ContentChecker::FEATURE_STATUS_WD |
| 138 |
} |
| 139 |
|
| 140 |
## TODO: According to HTML4 definition, authors SHOULD use style sheets |
| 141 |
## rather than presentational attributes (deprecated or not deprecated). |
| 142 |
|
| 143 |
## NOTE: Diff from HTML4. |
| 144 |
sub FEATURE_HTML32_REC_OBSOLETE () { |
| 145 |
Whatpm::ContentChecker::FEATURE_STATUS_CR | |
| 146 |
Whatpm::ContentChecker::FEATURE_DEPRECATED_SHOULD |
| 147 |
## NOTE: Lowercase normative "should". |
| 148 |
} |
| 149 |
|
| 150 |
sub FEATURE_RFC2659 () { ## Experimental RFC |
| 151 |
Whatpm::ContentChecker::FEATURE_STATUS_CR |
| 152 |
} |
| 153 |
|
| 154 |
## NOTE: HTML 2.x - diff from HTML 2.0 and not in newer versions. |
| 155 |
sub FEATURE_HTML2X_RFC () { ## Proposed Standard, obsolete |
| 156 |
Whatpm::ContentChecker::FEATURE_STATUS_CR |
| 157 |
} |
| 158 |
|
| 159 |
## NOTE: Diff from HTML 2.0. |
| 160 |
sub FEATURE_RFC1942 () { ## Experimental RFC, obsolete |
| 161 |
Whatpm::ContentChecker::FEATURE_STATUS_CR |
| 162 |
} |
| 163 |
|
| 164 |
## NOTE: Diff from HTML 3.2. |
| 165 |
sub FEATURE_HTML20_RFC () { ## Proposed Standard, obsolete |
| 166 |
Whatpm::ContentChecker::FEATURE_STATUS_CR |
| 167 |
} |
| 168 |
|
| 169 |
## December 2007 HTML5 Classification |
| 170 |
|
| 171 |
my $HTMLMetadataContent = { |
| 172 |
$HTML_NS => { |
| 173 |
title => 1, base => 1, link => 1, style => 1, script => 1, noscript => 1, |
| 174 |
'event-source' => 1, eventsource => 1, |
| 175 |
command => 1, datatemplate => 1, |
| 176 |
## NOTE: A |meta| with no |name| element is not allowed as |
| 177 |
## a metadata content other than |head| element. |
| 178 |
meta => 1, |
| 179 |
}, |
| 180 |
## NOTE: RDF is mentioned in the HTML5 spec. |
| 181 |
## TODO: Other RDF elements? |
| 182 |
q<http://www.w3.org/1999/02/22-rdf-syntax-ns#> => {RDF => 1}, |
| 183 |
}; |
| 184 |
|
| 185 |
my $HTMLFlowContent = { |
| 186 |
$HTML_NS => { |
| 187 |
section => 1, nav => 1, article => 1, blockquote => 1, aside => 1, |
| 188 |
h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1, header => 1, |
| 189 |
footer => 1, address => 1, p => 1, hr => 1, dialog => 1, pre => 1, |
| 190 |
ol => 1, ul => 1, dl => 1, figure => 1, map => 1, table => 1, |
| 191 |
form => 1, fieldset => 1, |
| 192 |
details => 1, ## ISSUE: "Flow element" in spec. |
| 193 |
datagrid => 1, ## ISSUE: "Flow element" in spec. |
| 194 |
datatemplate => 1, |
| 195 |
div => 1, ## ISSUE: No category in spec. |
| 196 |
## NOTE: |style| is only allowed if |scoped| attribute is specified. |
| 197 |
## Additionally, it must be before any other element or |
| 198 |
## non-inter-element-whitespace text node. |
| 199 |
style => 1, |
| 200 |
|
| 201 |
br => 1, q => 1, cite => 1, em => 1, strong => 1, small => 1, mark => 1, |
| 202 |
dfn => 1, abbr => 1, time => 1, progress => 1, meter => 1, code => 1, |
| 203 |
var => 1, samp => 1, kbd => 1, sub => 1, sup => 1, span => 1, i => 1, |
| 204 |
b => 1, bdo => 1, ruby => 1, |
| 205 |
script => 1, noscript => 1, 'event-source' => 1, eventsource => 1, |
| 206 |
command => 1, bb => 1, |
| 207 |
input => 1, button => 1, label => 1, select => 1, datalist => 1, |
| 208 |
textarea => 1, output => 1, |
| 209 |
datagrid => 1, ## ISSUE: "Interactive element" in the spec. |
| 210 |
## NOTE: |area| is allowed only as a descendant of |map|. |
| 211 |
area => 1, |
| 212 |
|
| 213 |
## NOTE: Transparent. |
| 214 |
a => 1, ins => 1, del => 1, font => 1, |
| 215 |
|
| 216 |
## NOTE: If there is a |menu| ancestor, phrasing. Otherwise, flow. |
| 217 |
menu => 1, |
| 218 |
|
| 219 |
img => 1, iframe => 1, embed => 1, object => 1, video => 1, audio => 1, |
| 220 |
canvas => 1, |
| 221 |
}, |
| 222 |
|
| 223 |
## NOTE: Embedded |
| 224 |
q<http://www.w3.org/1998/Math/MathML> => {math => 1}, |
| 225 |
q<http://www.w3.org/2000/svg> => {svg => 1}, |
| 226 |
}; |
| 227 |
|
| 228 |
my $HTMLSectioningContent = { |
| 229 |
$HTML_NS => { |
| 230 |
section => 1, nav => 1, article => 1, aside => 1, |
| 231 |
## NOTE: |body| is only allowed in |html| element. |
| 232 |
body => 1, |
| 233 |
}, |
| 234 |
}; |
| 235 |
|
| 236 |
my $HTMLSectioningRoot = { |
| 237 |
$HTML_NS => { |
| 238 |
blockquote => 1, datagrid => 1, figure => 1, td => 1, |
| 239 |
}, |
| 240 |
}; |
| 241 |
|
| 242 |
my $HTMLHeadingContent = { |
| 243 |
$HTML_NS => { |
| 244 |
h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1, header => 1, |
| 245 |
}, |
| 246 |
}; |
| 247 |
|
| 248 |
my $HTMLPhrasingContent = { |
| 249 |
## NOTE: All phrasing content is also flow content. |
| 250 |
$HTML_NS => { |
| 251 |
br => 1, q => 1, cite => 1, em => 1, strong => 1, small => 1, mark => 1, |
| 252 |
dfn => 1, abbr => 1, time => 1, progress => 1, meter => 1, code => 1, |
| 253 |
var => 1, samp => 1, kbd => 1, sub => 1, sup => 1, span => 1, i => 1, |
| 254 |
b => 1, bdo => 1, ruby => 1, |
| 255 |
script => 1, noscript => 1, 'event-source' => 1, eventsource => 1, |
| 256 |
command => 1, bb => 1, |
| 257 |
input => 1, button => 1, label => 1, select => 1, datalist => 1, |
| 258 |
textarea => 1, output => 1, |
| 259 |
datagrid => 1, ## ISSUE: "Interactive element" in the spec. |
| 260 |
## NOTE: |area| is allowed only as a descendant of |map|. |
| 261 |
area => 1, |
| 262 |
|
| 263 |
## NOTE: Transparent. |
| 264 |
a => 1, ins => 1, del => 1, font => 1, |
| 265 |
|
| 266 |
## NOTE: If there is a |menu| ancestor, phrasing. Otherwise, flow. |
| 267 |
menu => 1, |
| 268 |
|
| 269 |
img => 1, iframe => 1, embed => 1, object => 1, video => 1, audio => 1, |
| 270 |
canvas => 1, |
| 271 |
}, |
| 272 |
|
| 273 |
## NOTE: Embedded |
| 274 |
q<http://www.w3.org/1998/Math/MathML> => {math => 1}, |
| 275 |
q<http://www.w3.org/2000/svg> => {svg => 1}, |
| 276 |
|
| 277 |
## NOTE: And non-inter-element-whitespace text nodes. |
| 278 |
}; |
| 279 |
|
| 280 |
## $HTMLEmbeddedContent: See Whatpm::ContentChecker. |
| 281 |
|
| 282 |
my $HTMLInteractiveContent = { |
| 283 |
$HTML_NS => { |
| 284 |
a => 1, |
| 285 |
label => 1, input => 1, button => 1, select => 1, textarea => 1, |
| 286 |
details => 1, datagrid => 1, bb => 1, |
| 287 |
|
| 288 |
## NOTE: When "controls" attribute is specified. |
| 289 |
video => 1, audio => 1, |
| 290 |
|
| 291 |
## NOTE: When "type=toolbar" attribute is specified. |
| 292 |
menu => 1, |
| 293 |
}, |
| 294 |
}; |
| 295 |
|
| 296 |
## NOTE: Labelable form-associated element. |
| 297 |
my $LabelableFAE = { |
| 298 |
$HTML_NS => { |
| 299 |
input => 1, button => 1, select => 1, textarea => 1, |
| 300 |
}, |
| 301 |
}; |
| 302 |
|
| 303 |
our $IsInHTMLInteractiveContent; # See Whatpm::ContentChecker. |
| 304 |
|
| 305 |
## NOTE: $HTMLTransparentElements: See Whatpm::ContentChecker. |
| 306 |
## NOTE: Semi-transparent elements: See Whatpm::ContentChecker. |
| 307 |
|
| 308 |
## -- Common attribute syntacx checkers |
| 309 |
|
| 310 |
our $AttrChecker; |
| 311 |
our $AttrStatus; |
| 312 |
|
| 313 |
my $GetHTMLEnumeratedAttrChecker = sub { |
| 314 |
my $states = shift; # {value => conforming ? 1 : -1} |
| 315 |
return sub { |
| 316 |
my ($self, $attr) = @_; |
| 317 |
my $value = lc $attr->value; ## TODO: ASCII case insensitibility? |
| 318 |
if ($states->{$value} > 0) { |
| 319 |
# |
| 320 |
} elsif ($states->{$value}) { |
| 321 |
$self->{onerror}->(node => $attr, type => 'enumerated:non-conforming', |
| 322 |
level => $self->{level}->{must}); |
| 323 |
} else { |
| 324 |
$self->{onerror}->(node => $attr, type => 'enumerated:invalid', |
| 325 |
level => $self->{level}->{must}); |
| 326 |
} |
| 327 |
}; |
| 328 |
}; # $GetHTMLEnumeratedAttrChecker |
| 329 |
|
| 330 |
my $GetHTMLBooleanAttrChecker = sub { |
| 331 |
my $local_name = shift; |
| 332 |
return sub { |
| 333 |
my ($self, $attr) = @_; |
| 334 |
my $value = lc $attr->value; ## TODO: case |
| 335 |
unless ($value eq $local_name or $value eq '') { |
| 336 |
$self->{onerror}->(node => $attr, type => 'boolean:invalid', |
| 337 |
level => $self->{level}->{must}); |
| 338 |
} |
| 339 |
}; |
| 340 |
}; # $GetHTMLBooleanAttrChecker |
| 341 |
|
| 342 |
## Unordered set of space-separated tokens |
| 343 |
my $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker = sub { |
| 344 |
my $allowed_words = shift; |
| 345 |
return sub { |
| 346 |
my ($self, $attr) = @_; |
| 347 |
my %word; |
| 348 |
for my $word (grep {length $_} |
| 349 |
split /[\x09\x0A\x0C\x0D\x20]+/, $attr->value) { |
| 350 |
unless ($word{$word}) { |
| 351 |
$word{$word} = 1; |
| 352 |
if (not defined $allowed_words or |
| 353 |
$allowed_words->{$word}) { |
| 354 |
# |
| 355 |
} else { |
| 356 |
$self->{onerror}->(node => $attr, type => 'word not allowed', |
| 357 |
value => $word, |
| 358 |
level => $self->{level}->{must}); |
| 359 |
} |
| 360 |
} else { |
| 361 |
$self->{onerror}->(node => $attr, type => 'duplicate token', |
| 362 |
value => $word, |
| 363 |
level => $self->{level}->{must}); |
| 364 |
} |
| 365 |
} |
| 366 |
}; |
| 367 |
}; # $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker |
| 368 |
|
| 369 |
## |rel| attribute (set of space separated tokens, |
| 370 |
## whose allowed values are defined by the section on link types) |
| 371 |
my $HTMLLinkTypesAttrChecker = sub { |
| 372 |
my ($a_or_area, $todo, $self, $attr, $item, $element_state) = @_; |
| 373 |
my %word; |
| 374 |
for my $word (grep {length $_} |
| 375 |
split /[\x09\x0A\x0C\x0D\x20]+/, $attr->value) { |
| 376 |
unless ($word{$word}) { |
| 377 |
$word{$word} = 1; |
| 378 |
} elsif ($word eq 'up') { |
| 379 |
# |
| 380 |
} else { |
| 381 |
$self->{onerror}->(node => $attr, type => 'duplicate token', |
| 382 |
value => $word, |
| 383 |
level => $self->{level}->{must}); |
| 384 |
} |
| 385 |
} |
| 386 |
## NOTE: Case sensitive match (since HTML5 spec does not say link |
| 387 |
## types are case-insensitive and it says "The value should not |
| 388 |
## be confusingly similar to any other defined value (e.g. |
| 389 |
## differing only in case)."). |
| 390 |
## NOTE: Though there is no explicit "MUST NOT" for undefined values, |
| 391 |
## "MAY"s and "only ... MAY" restrict non-standard non-registered |
| 392 |
## values to be used conformingly. |
| 393 |
|
| 394 |
my $is_hyperlink; |
| 395 |
my $is_resource; |
| 396 |
require Whatpm::_LinkTypeList; |
| 397 |
our $LinkType; |
| 398 |
for my $word (keys %word) { |
| 399 |
my $def = $LinkType->{$word}; |
| 400 |
if (defined $def) { |
| 401 |
if ($def->{status} eq 'accepted') { |
| 402 |
if (defined $def->{effect}->[$a_or_area]) { |
| 403 |
# |
| 404 |
} else { |
| 405 |
$self->{onerror}->(node => $attr, |
| 406 |
type => 'link type:bad context', |
| 407 |
value => $word, |
| 408 |
level => $self->{level}->{must}); |
| 409 |
} |
| 410 |
} elsif ($def->{status} eq 'proposal') { |
| 411 |
$self->{onerror}->(node => $attr, |
| 412 |
type => 'link type:proposed', |
| 413 |
value => $word, |
| 414 |
level => $self->{level}->{should}); |
| 415 |
if (defined $def->{effect}->[$a_or_area]) { |
| 416 |
# |
| 417 |
} else { |
| 418 |
$self->{onerror}->(node => $attr, |
| 419 |
type => 'link type:bad context', |
| 420 |
value => $word, |
| 421 |
level => $self->{level}->{must}); |
| 422 |
} |
| 423 |
} else { # rejected or synonym |
| 424 |
$self->{onerror}->(node => $attr, |
| 425 |
type => 'link type:non-conforming', |
| 426 |
value => $word, |
| 427 |
level => $self->{level}->{must}); |
| 428 |
} |
| 429 |
if (defined $def->{effect}->[$a_or_area]) { |
| 430 |
if ($word eq 'alternate') { |
| 431 |
# |
| 432 |
} elsif ($def->{effect}->[$a_or_area] eq 'hyperlink') { |
| 433 |
$is_hyperlink = 1; |
| 434 |
} |
| 435 |
} |
| 436 |
if ($def->{unique}) { |
| 437 |
unless ($self->{has_link_type}->{$word}) { |
| 438 |
$self->{has_link_type}->{$word} = 1; |
| 439 |
} else { |
| 440 |
$self->{onerror}->(node => $attr, |
| 441 |
type => 'link type:duplicate', |
| 442 |
value => $word, |
| 443 |
level => $self->{level}->{must}); |
| 444 |
} |
| 445 |
} |
| 446 |
|
| 447 |
if (defined $def->{effect}->[$a_or_area] and $word ne 'alternate') { |
| 448 |
$is_hyperlink = 1 if $def->{effect}->[$a_or_area] eq 'hyperlink'; |
| 449 |
$is_resource = 1 if $def->{effect}->[$a_or_area] eq 'external resource'; |
| 450 |
} |
| 451 |
} else { |
| 452 |
$self->{onerror}->(node => $attr, |
| 453 |
type => 'unknown link type', |
| 454 |
value => $word, |
| 455 |
level => $self->{level}->{uncertain}); |
| 456 |
} |
| 457 |
} |
| 458 |
$is_hyperlink = 1 if $word{alternate} and not $word{stylesheet}; |
| 459 |
## TODO: The Pingback 1.0 specification, which is referenced by HTML5, |
| 460 |
## says that using both X-Pingback: header field and HTML |
| 461 |
## <link rel=pingback> is deprecated and if both appears they |
| 462 |
## SHOULD contain exactly the same value. |
| 463 |
## ISSUE: Pingback 1.0 specification defines the exact representation |
| 464 |
## of its link element, which cannot be tested by the current arch. |
| 465 |
## ISSUE: Pingback 1.0 specification says that the document MUST NOT |
| 466 |
## include any string that matches to the pattern for the rel=pingback link, |
| 467 |
## which again inpossible to test. |
| 468 |
## ISSUE: rel=pingback href MUST NOT include entities other than predefined 4. |
| 469 |
|
| 470 |
## NOTE: <link rel="up index"><link rel="up up index"> is not an error. |
| 471 |
## NOTE: We can't check "If the page is part of multiple hierarchies, |
| 472 |
## then they SHOULD be described in different paragraphs.". |
| 473 |
|
| 474 |
$todo->{has_hyperlink_link_type} = 1 if $is_hyperlink; |
| 475 |
if ($is_hyperlink or $a_or_area) { |
| 476 |
$element_state->{uri_info}->{href}->{type}->{hyperlink} = 1; |
| 477 |
} |
| 478 |
if ($is_resource and not $a_or_area) { |
| 479 |
$element_state->{uri_info}->{href}->{type}->{resource} = 1; |
| 480 |
} |
| 481 |
|
| 482 |
$element_state->{link_rel} = \%word; |
| 483 |
}; # $HTMLLinkTypesAttrChecker |
| 484 |
|
| 485 |
## TODO: "When an author uses a new type not defined by either this specification or the Wiki page, conformance checkers should offer to add the value to the Wiki, with the details described above, with the "proposal" status." |
| 486 |
|
| 487 |
## URI (or IRI) |
| 488 |
my $HTMLURIAttrChecker = sub { |
| 489 |
my ($self, $attr, $item, $element_state) = @_; |
| 490 |
## ISSUE: Relative references are allowed? (RFC 3987 "IRI" is an absolute reference with optional fragment identifier.) |
| 491 |
my $value = $attr->value; |
| 492 |
Whatpm::URIChecker->check_iri_reference ($value, sub { |
| 493 |
$self->{onerror}->(@_, node => $attr); |
| 494 |
}), $self->{level}; |
| 495 |
$self->{has_uri_attr} = 1; ## TODO: <html manifest> |
| 496 |
|
| 497 |
my $attr_name = $attr->name; |
| 498 |
$element_state->{uri_info}->{$attr_name}->{node} = $attr; |
| 499 |
## TODO: absolute |
| 500 |
push @{$self->{return}->{uri}->{$value} ||= []}, |
| 501 |
$element_state->{uri_info}->{$attr_name}; |
| 502 |
}; # $HTMLURIAttrChecker |
| 503 |
|
| 504 |
## A space separated list of one or more URIs (or IRIs) |
| 505 |
my $HTMLSpaceURIsAttrChecker = sub { |
| 506 |
my ($self, $attr) = @_; |
| 507 |
|
| 508 |
my $type = {ping => 'action', |
| 509 |
profile => 'namespace', |
| 510 |
archive => 'resource'}->{$attr->name}; |
| 511 |
|
| 512 |
my $i = 0; |
| 513 |
for my $value (split /[\x09\x0A\x0C\x0D\x20]+/, $attr->value) { |
| 514 |
Whatpm::URIChecker->check_iri_reference ($value, sub { |
| 515 |
$self->{onerror}->(value => $value, @_, node => $attr, index => $i); |
| 516 |
}, $self->{level}); |
| 517 |
|
| 518 |
## TODO: absolute |
| 519 |
push @{$self->{return}->{uri}->{$value} ||= []}, |
| 520 |
{node => $attr, type => {$type => 1}}; |
| 521 |
|
| 522 |
$i++; |
| 523 |
} |
| 524 |
## ISSUE: Relative references? (especially, in profile="") |
| 525 |
## ISSUE: Leading or trailing white spaces are conformant? |
| 526 |
## ISSUE: A sequence of white space characters are conformant? |
| 527 |
## ISSUE: A zero-length string is conformant? (It does contain a relative reference, i.e. same as base URI.) |
| 528 |
## ISSUE: What is "space"? |
| 529 |
## NOTE: Duplication seems not an error. |
| 530 |
$self->{has_uri_attr} = 1; |
| 531 |
}; # $HTMLSpaceURIsAttrChecker |
| 532 |
|
| 533 |
## NOTE: "Valid datetime". |
| 534 |
my $HTMLDatetimeAttrChecker = sub { |
| 535 |
my ($self, $attr) = @_; |
| 536 |
my $value = $attr->value; |
| 537 |
## ISSUE: "space", not "space character" (in parsing algorihtm, "space character") |
| 538 |
if ($value =~ /\A([0-9]{4})-([0-9]{2})-([0-9]{2}) |
| 539 |
(?>[\x09\x0A\x0C\x0D\x20]+ |
| 540 |
(?>T[\x09\x0A\x0C\x0D\x20]*)?|T[\x09\x0A\x0C\x0D\x20]*) |
| 541 |
([0-9]{2}):([0-9]{2})(?>:([0-9]{2}))?(?>\.([0-9]+))? |
| 542 |
[\x09\x0A\x0C\x0D\x20]* |
| 543 |
(?>Z|[+-]([0-9]{2}):([0-9]{2}))\z/x) { |
| 544 |
my ($y, $M, $d, $h, $m, $s, $f, $zh, $zm) |
| 545 |
= ($1, $2, $3, $4, $5, $6, $7, $8, $9); |
| 546 |
if (0 < $M and $M < 13) { ## ISSUE: This is not explicitly specified (though in parsing algorithm) |
| 547 |
$self->{onerror}->(node => $attr, type => 'datetime:bad day', |
| 548 |
level => $self->{level}->{must}) |
| 549 |
if $d < 1 or |
| 550 |
$d > [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]->[$M]; |
| 551 |
$self->{onerror}->(node => $attr, type => 'datetime:bad day', |
| 552 |
level => $self->{level}->{must}) |
| 553 |
if $M == 2 and $d == 29 and |
| 554 |
not ($y % 400 == 0 or ($y % 4 == 0 and $y % 100 != 0)); |
| 555 |
} else { |
| 556 |
$self->{onerror}->(node => $attr, type => 'datetime:bad month', |
| 557 |
level => $self->{level}->{must}); |
| 558 |
} |
| 559 |
$self->{onerror}->(node => $attr, type => 'datetime:bad hour', |
| 560 |
level => $self->{level}->{must}) if $h > 23; |
| 561 |
$self->{onerror}->(node => $attr, type => 'datetime:bad minute', |
| 562 |
level => $self->{level}->{must}) if $m > 59; |
| 563 |
$self->{onerror}->(node => $attr, type => 'datetime:bad second', |
| 564 |
level => $self->{level}->{must}) |
| 565 |
if defined $s and $s > 59; |
| 566 |
$self->{onerror}->(node => $attr, type => 'datetime:bad timezone hour', |
| 567 |
level => $self->{level}->{must}) if $zh > 23; |
| 568 |
$self->{onerror}->(node => $attr, type => 'datetime:bad timezone minute', |
| 569 |
level => $self->{level}->{must}) if $zm > 59; |
| 570 |
## ISSUE: Maybe timezone -00:00 should have same semantics as in RFC 3339. |
| 571 |
} else { |
| 572 |
$self->{onerror}->(node => $attr, type => 'datetime:syntax error', |
| 573 |
level => $self->{level}->{must}); |
| 574 |
} |
| 575 |
}; # $HTMLDatetimeAttrChecker |
| 576 |
|
| 577 |
my $HTMLIntegerAttrChecker = sub { |
| 578 |
my ($self, $attr) = @_; |
| 579 |
my $value = $attr->value; |
| 580 |
unless ($value =~ /\A-?[0-9]+\z/) { |
| 581 |
$self->{onerror}->(node => $attr, type => 'integer:syntax error', |
| 582 |
level => $self->{level}->{must}); |
| 583 |
} |
| 584 |
}; # $HTMLIntegerAttrChecker |
| 585 |
|
| 586 |
my $GetHTMLNonNegativeIntegerAttrChecker = sub { |
| 587 |
my $range_check = shift; |
| 588 |
return sub { |
| 589 |
my ($self, $attr) = @_; |
| 590 |
my $value = $attr->value; |
| 591 |
if ($value =~ /\A[0-9]+\z/) { |
| 592 |
unless ($range_check->($value + 0)) { |
| 593 |
$self->{onerror}->(node => $attr, type => 'nninteger:out of range', |
| 594 |
level => $self->{level}->{must}); |
| 595 |
} |
| 596 |
} else { |
| 597 |
$self->{onerror}->(node => $attr, |
| 598 |
type => 'nninteger:syntax error', |
| 599 |
level => $self->{level}->{must}); |
| 600 |
} |
| 601 |
}; |
| 602 |
}; # $GetHTMLNonNegativeIntegerAttrChecker |
| 603 |
|
| 604 |
my $GetHTMLFloatingPointNumberAttrChecker = sub { |
| 605 |
my $range_check = shift; |
| 606 |
return sub { |
| 607 |
my ($self, $attr) = @_; |
| 608 |
my $value = $attr->value; |
| 609 |
if ($value =~ /\A-?[0-9]+(?>\.[0-9]*)?\z/ or |
| 610 |
$value =~ /\A-?\.[0-9]+\z/) { |
| 611 |
unless ($range_check->($value + 0)) { |
| 612 |
$self->{onerror}->(node => $attr, type => 'float:out of range', |
| 613 |
level => $self->{level}->{must}); |
| 614 |
} |
| 615 |
} else { |
| 616 |
$self->{onerror}->(node => $attr, |
| 617 |
type => 'float:syntax error', |
| 618 |
level => $self->{level}->{must}); |
| 619 |
} |
| 620 |
}; |
| 621 |
|
| 622 |
## TODO: scientific notation |
| 623 |
}; # $GetHTMLFloatingPointNumberAttrChecker |
| 624 |
|
| 625 |
my $StepAttrChecker = sub { |
| 626 |
## NOTE: A valid floating point number (> 0), or ASCII |
| 627 |
## case-insensitive "any". |
| 628 |
|
| 629 |
my ($self, $attr) = @_; |
| 630 |
my $value = $attr->value; |
| 631 |
if ($value =~ /\A-?[0-9]+(?>\.[0-9]*)?\z/ or |
| 632 |
$value =~ /\A-?\.[0-9]+\z/) { |
| 633 |
unless ($value > 0) { |
| 634 |
$self->{onerror}->(node => $attr, type => 'float:out of range', |
| 635 |
level => $self->{level}->{must}); |
| 636 |
} |
| 637 |
} elsif ($value =~ /\A[Aa][Nn][Yy]\z/) { |
| 638 |
# |
| 639 |
} else { |
| 640 |
$self->{onerror}->(node => $attr, |
| 641 |
type => 'float:syntax error', |
| 642 |
level => $self->{level}->{must}); |
| 643 |
} |
| 644 |
|
| 645 |
## TODO: scientific |
| 646 |
}; # $StepAttrChecker |
| 647 |
|
| 648 |
## HTML4 %Length; |
| 649 |
my $HTMLLengthAttrChecker = sub { |
| 650 |
my ($self, $attr) = @_; |
| 651 |
my $value = $attr->value; |
| 652 |
unless ($value =~ /\A[0-9]+%?\z/) { |
| 653 |
$self->{onerror}->(node => $attr, type => 'length:syntax error', |
| 654 |
level => $self->{level}->{must}); |
| 655 |
} |
| 656 |
|
| 657 |
## NOTE: HTML4 definition is too vague - it does not define the syntax |
| 658 |
## of percentage value at all (!). |
| 659 |
}; # $HTMLLengthAttrChecker |
| 660 |
|
| 661 |
## "A valid MIME type, optionally with parameters. [RFC 2046]" |
| 662 |
## ISSUE: RFC 2046 does not define syntax of media types. |
| 663 |
## ISSUE: The definition of "a valid MIME type" is unknown. |
| 664 |
## Syntactical correctness? |
| 665 |
my $HTMLIMTAttrChecker = sub { |
| 666 |
my ($self, $attr) = @_; |
| 667 |
my $value = $attr->value; |
| 668 |
## ISSUE: RFC 2045 Content-Type header field allows insertion |
| 669 |
## of LWS/comments between tokens. Is it allowed in HTML? Maybe no. |
| 670 |
## ISSUE: RFC 2231 extension? Maybe no. |
| 671 |
my $lws0 = qr/(?>(?>\x0D\x0A)?[\x09\x20])*/; |
| 672 |
my $token = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+/; |
| 673 |
my $qs = qr/"(?>[\x00-\x0C\x0E-\x21\x23-\x5B\x5D-\x7E]|\x0D\x0A[\x09\x20]|\x5C[\x00-\x7F])*"/; |
| 674 |
if ($value =~ m#\A$lws0($token)$lws0/$lws0($token)$lws0((?>;$lws0$token$lws0=$lws0(?>$token|$qs)$lws0)*)\z#) { |
| 675 |
my @type = ($1, $2); |
| 676 |
my $param = $3; |
| 677 |
while ($param =~ s/^;$lws0($token)$lws0=$lws0(?>($token)|($qs))$lws0//) { |
| 678 |
if (defined $2) { |
| 679 |
push @type, $1 => $2; |
| 680 |
} else { |
| 681 |
my $n = $1; |
| 682 |
my $v = $2; |
| 683 |
$v =~ s/\\(.)/$1/gs; |
| 684 |
push @type, $n => $v; |
| 685 |
} |
| 686 |
} |
| 687 |
require Whatpm::IMTChecker; |
| 688 |
my $ic = Whatpm::IMTChecker->new; |
| 689 |
$ic->{level} = $self->{level}; |
| 690 |
$ic->check_imt (sub { |
| 691 |
$self->{onerror}->(@_, node => $attr); |
| 692 |
}, @type); |
| 693 |
} else { |
| 694 |
$self->{onerror}->(node => $attr, type => 'IMT:syntax error', |
| 695 |
level => $self->{level}->{must}); |
| 696 |
} |
| 697 |
}; # $HTMLIMTAttrChecker |
| 698 |
|
| 699 |
my $HTMLLanguageTagAttrChecker = sub { |
| 700 |
## NOTE: See also $AtomLanguageTagAttrChecker in Atom.pm. |
| 701 |
|
| 702 |
my ($self, $attr) = @_; |
| 703 |
my $value = $attr->value; |
| 704 |
require Whatpm::LangTag; |
| 705 |
Whatpm::LangTag->check_rfc3066_language_tag ($value, sub { |
| 706 |
$self->{onerror}->(@_, node => $attr); |
| 707 |
}, $self->{level}); |
| 708 |
## ISSUE: RFC 4646 (3066bis)? |
| 709 |
|
| 710 |
## TODO: testdata |
| 711 |
}; # $HTMLLanguageTagAttrChecker |
| 712 |
|
| 713 |
## "A valid media query [MQ]" |
| 714 |
my $HTMLMQAttrChecker = sub { |
| 715 |
my ($self, $attr) = @_; |
| 716 |
$self->{onerror}->(node => $attr, |
| 717 |
type => 'media query', |
| 718 |
level => $self->{level}->{uncertain}); |
| 719 |
## ISSUE: What is "a valid media query"? |
| 720 |
}; # $HTMLMQAttrChecker |
| 721 |
|
| 722 |
my $HTMLEventHandlerAttrChecker = sub { |
| 723 |
my ($self, $attr) = @_; |
| 724 |
$self->{onerror}->(node => $attr, |
| 725 |
type => 'event handler', |
| 726 |
level => $self->{level}->{uncertain}); |
| 727 |
## TODO: MUST contain valid ECMAScript code matching the |
| 728 |
## ECMAScript |FunctionBody| production. [ECMA262] |
| 729 |
## ISSUE: MUST be ES3? E4X? ES4? JS1.x? |
| 730 |
## ISSUE: Automatic semicolon insertion does not apply? |
| 731 |
## ISSUE: Other script languages? |
| 732 |
}; # $HTMLEventHandlerAttrChecker |
| 733 |
|
| 734 |
my $HTMLFormAttrChecker = sub { |
| 735 |
my ($self, $attr) = @_; |
| 736 |
|
| 737 |
## NOTE: MUST be the ID of a |form| element. |
| 738 |
|
| 739 |
my $value = $attr->value; |
| 740 |
push @{$self->{idref}}, ['form', $value => $attr]; |
| 741 |
|
| 742 |
## ISSUE: <form id=""><input form=""> (empty ID)? |
| 743 |
}; # $HTMLFormAttrChecker |
| 744 |
|
| 745 |
my $HTMLUsemapAttrChecker = sub { |
| 746 |
my ($self, $attr) = @_; |
| 747 |
## MUST be a valid hash-name reference to a |map| element. |
| 748 |
my $value = $attr->value; |
| 749 |
if ($value =~ s/^#//) { |
| 750 |
## NOTE: |usemap="#"| is conforming, though it identifies no |map| element |
| 751 |
## according to the "rules for parsing a hash-name reference" algorithm. |
| 752 |
## The document is non-conforming anyway, since |<map name="">| (empty |
| 753 |
## name) is non-conforming. |
| 754 |
push @{$self->{usemap}}, [$value => $attr]; |
| 755 |
} else { |
| 756 |
$self->{onerror}->(node => $attr, type => 'hashref:syntax error', |
| 757 |
level => $self->{level}->{must}); |
| 758 |
} |
| 759 |
## NOTE: Space characters in hash-name references are conforming. |
| 760 |
## ISSUE: UA algorithm for matching is case-insensitive; IDs only different in cases should be reported |
| 761 |
}; # $HTMLUsemapAttrChecker |
| 762 |
|
| 763 |
## Valid browsing context name |
| 764 |
my $HTMLBrowsingContextNameAttrChecker = sub { |
| 765 |
my ($self, $attr) = @_; |
| 766 |
my $value = $attr->value; |
| 767 |
if ($value =~ /^_/) { |
| 768 |
$self->{onerror}->(node => $attr, type => 'window name:reserved', |
| 769 |
level => $self->{level}->{must}, |
| 770 |
value => $value); |
| 771 |
} elsif (length $value) { |
| 772 |
# |
| 773 |
} else { |
| 774 |
$self->{onerror}->(node => $attr, type => 'window name:empty', |
| 775 |
level => $self->{level}->{must}); |
| 776 |
} |
| 777 |
}; # $HTMLBrowsingContextNameAttrChecker |
| 778 |
|
| 779 |
## Valid browsing context name or keyword |
| 780 |
my $HTMLTargetAttrChecker = sub { |
| 781 |
my ($self, $attr) = @_; |
| 782 |
my $value = $attr->value; |
| 783 |
if ($value =~ /^_/) { |
| 784 |
$value = lc $value; ## ISSUE: ASCII case-insentitive? |
| 785 |
unless ({ |
| 786 |
_blank => 1,_self => 1, _parent => 1, _top => 1, |
| 787 |
}->{$value}) { |
| 788 |
$self->{onerror}->(node => $attr, |
| 789 |
type => 'window name:reserved', |
| 790 |
level => $self->{level}->{must}, |
| 791 |
value => $value); |
| 792 |
} |
| 793 |
} elsif (length $value) { |
| 794 |
# |
| 795 |
} else { |
| 796 |
$self->{onerror}->(node => $attr, type => 'window name:empty', |
| 797 |
level => $self->{level}->{must}); |
| 798 |
} |
| 799 |
}; # $HTMLTargetAttrChecker |
| 800 |
|
| 801 |
my $HTMLSelectorsAttrChecker = sub { |
| 802 |
my ($self, $attr) = @_; |
| 803 |
|
| 804 |
## ISSUE: Namespace resolution? |
| 805 |
|
| 806 |
my $value = $attr->value; |
| 807 |
|
| 808 |
require Whatpm::CSS::SelectorsParser; |
| 809 |
my $p = Whatpm::CSS::SelectorsParser->new; |
| 810 |
$p->{pseudo_class}->{$_} = 1 for qw/ |
| 811 |
active checked disabled empty enabled first-child first-of-type |
| 812 |
focus hover indeterminate last-child last-of-type link only-child |
| 813 |
only-of-type root target visited |
| 814 |
lang nth-child nth-last-child nth-of-type nth-last-of-type not |
| 815 |
-manakai-contains -manakai-current |
| 816 |
/; |
| 817 |
|
| 818 |
$p->{pseudo_element}->{$_} = 1 for qw/ |
| 819 |
after before first-letter first-line |
| 820 |
/; |
| 821 |
|
| 822 |
$p->{level} = $self->{level}; |
| 823 |
$p->{onerror} = sub { |
| 824 |
$self->{onerror}->(@_, node => $attr); |
| 825 |
}; |
| 826 |
$p->parse_string ($value); |
| 827 |
}; # $HTMLSelectorsAttrChecker |
| 828 |
|
| 829 |
my $HTMLAccesskeyAttrChecker = sub { |
| 830 |
my ($self, $attr) = @_; |
| 831 |
|
| 832 |
## NOTE: "character" or |%Character;| in HTML4. |
| 833 |
|
| 834 |
my $value = $attr->value; |
| 835 |
if (length $value != 1) { |
| 836 |
$self->{onerror}->(node => $attr, type => 'char:syntax error', |
| 837 |
level => $self->{level}->{html4_fact}); |
| 838 |
} |
| 839 |
|
| 840 |
## NOTE: "Note. Authors should consider the input method of the expected |
| 841 |
## reader when specifying an accesskey." [HTML4] This is hard to implement, |
| 842 |
## since it depends on keyboard and so on. |
| 843 |
## NOTE: "We recommend that authors include the access key in label text |
| 844 |
## or wherever the access key is to apply." [HTML4] (informative) |
| 845 |
}; # $HTMLAccesskeyAttrChecker |
| 846 |
|
| 847 |
my $HTMLCharsetChecker = sub ($$$;$) { |
| 848 |
my ($charset_value, $self, $attr, $ascii_compat) = @_; |
| 849 |
|
| 850 |
## NOTE: This code is used for |charset=""| attributes, |charset=| |
| 851 |
## portion of the |content=""| attributes, and |accept-charset=""| |
| 852 |
## attributes. |
| 853 |
|
| 854 |
## NOTE: Though the case-sensitivility of |charset| attribute value |
| 855 |
## is not explicitly spelled in the HTML5 spec, the Character Set |
| 856 |
## registry of IANA, which is referenced from HTML5 spec, says that |
| 857 |
## charset name is case-insensitive. |
| 858 |
$charset_value =~ tr/A-Z/a-z/; ## NOTE: ASCII Case-insensitive. |
| 859 |
|
| 860 |
require Message::Charset::Info; |
| 861 |
my $charset = $Message::Charset::Info::IANACharset->{$charset_value}; |
| 862 |
|
| 863 |
## ISSUE: What is "valid character encoding name"? Syntactically valid? |
| 864 |
## Syntactically valid and registered? What about x-charset names? |
| 865 |
unless (Message::Charset::Info::is_syntactically_valid_iana_charset_name |
| 866 |
($charset_value)) { |
| 867 |
$self->{onerror}->(node => $attr, |
| 868 |
type => 'charset:syntax error', |
| 869 |
value => $charset_value, |
| 870 |
level => $self->{level}->{must}); |
| 871 |
} |
| 872 |
|
| 873 |
if ($charset) { |
| 874 |
## ISSUE: What is "the preferred name for that encoding" (for a charset |
| 875 |
## with no "preferred MIME name" label)? |
| 876 |
my $charset_status = $charset->{iana_names}->{$charset_value} || 0; |
| 877 |
if (($charset_status & |
| 878 |
Message::Charset::Info::PREFERRED_CHARSET_NAME ()) |
| 879 |
!= Message::Charset::Info::PREFERRED_CHARSET_NAME ()) { |
| 880 |
$self->{onerror}->(node => $attr, |
| 881 |
type => 'charset:not preferred', |
| 882 |
value => $charset_value, |
| 883 |
level => $self->{level}->{must}); |
| 884 |
} |
| 885 |
|
| 886 |
if (($charset_status & |
| 887 |
Message::Charset::Info::REGISTERED_CHARSET_NAME ()) |
| 888 |
!= Message::Charset::Info::REGISTERED_CHARSET_NAME ()) { |
| 889 |
if ($charset_value =~ /^x-/) { |
| 890 |
$self->{onerror}->(node => $attr, |
| 891 |
type => 'charset:private', |
| 892 |
value => $charset_value, |
| 893 |
level => $self->{level}->{good}); |
| 894 |
} else { |
| 895 |
$self->{onerror}->(node => $attr, |
| 896 |
type => 'charset:not registered', |
| 897 |
value => $charset_value, |
| 898 |
level => $self->{level}->{good}); |
| 899 |
} |
| 900 |
} |
| 901 |
|
| 902 |
if ($ascii_compat) { |
| 903 |
if ($charset->{category} & |
| 904 |
Message::Charset::Info::CHARSET_CATEGORY_ASCII_COMPAT ()) { |
| 905 |
# |
| 906 |
} else { |
| 907 |
$self->{onerror}->(node => $attr, |
| 908 |
type => 'charset:not ascii compat', |
| 909 |
value => $charset_value, |
| 910 |
level => $self->{level}->{must}); |
| 911 |
} |
| 912 |
} |
| 913 |
|
| 914 |
## TODO: non-preferred-name error for following cases: |
| 915 |
} elsif ($charset_value =~ /^x-/) { |
| 916 |
$self->{onerror}->(node => $attr, |
| 917 |
type => 'charset:private', |
| 918 |
value => $charset_value, |
| 919 |
level => $self->{level}->{good}); |
| 920 |
|
| 921 |
## NOTE: Whether this is an ASCII-compatible character encoding or |
| 922 |
## not is unknown. |
| 923 |
} else { |
| 924 |
$self->{onerror}->(node => $attr, |
| 925 |
type => 'charset:not registered', |
| 926 |
value => $charset_value, |
| 927 |
level => $self->{level}->{good}); |
| 928 |
|
| 929 |
## NOTE: Whether this is an ASCII-compatible character encoding or |
| 930 |
## not is unknown. |
| 931 |
} |
| 932 |
|
| 933 |
return ($charset, $charset_value); |
| 934 |
}; # $HTMLCharsetChecker |
| 935 |
|
| 936 |
## NOTE: "An ordered set of space-separated tokens" where "each token |
| 937 |
## MUST be the preferred name of an ASCII-compatible character |
| 938 |
## encoding". |
| 939 |
my $HTMLCharsetsAttrChecker = sub { |
| 940 |
my ($self, $attr) = @_; |
| 941 |
|
| 942 |
## ISSUE: "ordered set of space-separated tokens" is not defined. |
| 943 |
|
| 944 |
my @value = grep {length $_} split /[\x09\x0A\x0C\x0D\x20]+/, $attr->value; |
| 945 |
|
| 946 |
## ISSUE: Uniqueness is not enforced. |
| 947 |
|
| 948 |
for my $charset (@value) { |
| 949 |
$HTMLCharsetChecker->($charset, $self, $attr, 1); |
| 950 |
} |
| 951 |
|
| 952 |
## ISSUE: Shift_JIS is ASCII-compatible? What about ISO-2022-JP? |
| 953 |
}; # $HTMLCharsetsAttrChecker |
| 954 |
|
| 955 |
my $HTMLColorAttrChecker = sub { |
| 956 |
my ($self, $attr) = @_; |
| 957 |
|
| 958 |
## NOTE: HTML4 "color" or |%Color;| |
| 959 |
|
| 960 |
my $value = $attr->value; |
| 961 |
|
| 962 |
if ($value !~ /\A(?>#[0-9A-F]+|black|silver|gray|white|maroon|red|purple|fuchsia|green|lime|olive|yellow|navy|blue|teal|aqua)\z/i) { |
| 963 |
$self->{onerror}->(node => $attr, type => 'color:syntax error', |
| 964 |
level => $self->{level}->{html4_fact}); |
| 965 |
} |
| 966 |
|
| 967 |
## TODO: HTML4 has some guideline on usage of color. |
| 968 |
}; # $HTMLColorAttrChecker |
| 969 |
|
| 970 |
my $HTMLRefOrTemplateAttrChecker = sub { |
| 971 |
my ($self, $attr) = @_; |
| 972 |
$HTMLURIAttrChecker->(@_); |
| 973 |
|
| 974 |
my $attr_name = $attr->name; |
| 975 |
|
| 976 |
if ($attr_name eq 'ref') { |
| 977 |
unless ($attr->owner_element->has_attribute_ns (undef, 'template')) { |
| 978 |
$self->{onerror}->(node => $attr, type => 'attribute not allowed', |
| 979 |
level => $self->{level}->{must}); |
| 980 |
} |
| 981 |
} |
| 982 |
|
| 983 |
my $doc = $attr->owner_document; |
| 984 |
my $doc_uri = $doc->document_uri; |
| 985 |
my $uri = $doc->implementation |
| 986 |
->create_uri_reference ($attr->value) |
| 987 |
->get_absolute_reference ($doc_uri); |
| 988 |
my $no_frag_uri = $uri->clone; |
| 989 |
$no_frag_uri->uri_fragment (undef); |
| 990 |
if ((defined $doc_uri and $doc_uri eq $no_frag_uri) or |
| 991 |
(not defined $doc_uri and $no_frag_uri eq '')) { |
| 992 |
my $fragid = $uri->uri_fragment; |
| 993 |
if (defined $fragid) { |
| 994 |
push @{$self->{$attr_name}}, [$fragid => $attr]; |
| 995 |
} else { |
| 996 |
DOCEL: { |
| 997 |
last DOCEL unless $attr_name eq 'template'; |
| 998 |
|
| 999 |
my $docel = $doc->document_element; |
| 1000 |
if ($docel) { |
| 1001 |
my $nsuri = $docel->namespace_uri; |
| 1002 |
if (defined $nsuri and $nsuri eq $HTML_NS) { |
| 1003 |
if ($docel->manakai_local_name eq 'datatemplate') { |
| 1004 |
last DOCEL; |
| 1005 |
} |
| 1006 |
} |
| 1007 |
} |
| 1008 |
|
| 1009 |
$self->{onerror}->(node => $attr, type => 'template:not template', |
| 1010 |
level => $self->{level}->{must}); |
| 1011 |
} # DOCEL |
| 1012 |
} |
| 1013 |
} else { |
| 1014 |
## TODO: An external document is referenced. |
| 1015 |
## The document MUST be an HTML or XML document. |
| 1016 |
## If there is a fragment identifier, it MUST point a part of the doc. |
| 1017 |
## If the attribute is |template|, the pointed part MUST be a |
| 1018 |
## |datatemplat| element. |
| 1019 |
## If no fragment identifier is specified, the root element MUST be |
| 1020 |
## a |datatemplate| element when the attribute is |template|. |
| 1021 |
} |
| 1022 |
}; # $HTMLRefOrTemplateAttrChecker |
| 1023 |
|
| 1024 |
my $HTMLRepeatIndexAttrChecker = sub { |
| 1025 |
my ($self, $attr) = @_; |
| 1026 |
|
| 1027 |
if (defined $attr->namespace_uri) { |
| 1028 |
my $oe = $attr->owner_element; |
| 1029 |
my $oe_nsuri = $oe->namespace_uri; |
| 1030 |
if (defined $oe_nsuri or $oe_nsuri eq $HTML_NS) { ## TODO: wrong? |
| 1031 |
$self->{onerror}->(node => $attr, type => 'attribute not allowed', |
| 1032 |
level => $self->{level}->{must}); |
| 1033 |
} |
| 1034 |
} |
| 1035 |
|
| 1036 |
$GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 })->(@_); |
| 1037 |
}; # $HTMLRepeatIndexAttrChecker |
| 1038 |
|
| 1039 |
my $HTMLAttrChecker = { |
| 1040 |
## TODO: aria-* ## TODO: svg:*/@aria-* [HTML5ROLE] -> [STATES] |
| 1041 |
id => sub { |
| 1042 |
my ($self, $attr, $item, $element_state) = @_; |
| 1043 |
my $value = $attr->value; |
| 1044 |
if (length $value > 0) { |
| 1045 |
if ($self->{id}->{$value}) { |
| 1046 |
$self->{onerror}->(node => $attr, type => 'duplicate ID', |
| 1047 |
level => $self->{level}->{must}); |
| 1048 |
push @{$self->{id}->{$value}}, $attr; |
| 1049 |
} else { |
| 1050 |
$self->{id}->{$value} = [$attr]; |
| 1051 |
$self->{id_type}->{$value} = $element_state->{id_type} || ''; |
| 1052 |
} |
| 1053 |
if ($value =~ /[\x09\x0A\x0C\x0D\x20]/) { |
| 1054 |
$self->{onerror}->(node => $attr, type => 'space in ID', |
| 1055 |
level => $self->{level}->{must}); |
| 1056 |
} |
| 1057 |
} else { |
| 1058 |
## NOTE: MUST contain at least one character |
| 1059 |
$self->{onerror}->(node => $attr, type => 'empty attribute value', |
| 1060 |
level => $self->{level}->{must}); |
| 1061 |
} |
| 1062 |
}, |
| 1063 |
title => sub {}, ## NOTE: No conformance creteria |
| 1064 |
lang => sub { |
| 1065 |
my ($self, $attr) = @_; |
| 1066 |
my $value = $attr->value; |
| 1067 |
if ($value eq '') { |
| 1068 |
# |
| 1069 |
} else { |
| 1070 |
require Whatpm::LangTag; |
| 1071 |
Whatpm::LangTag->check_rfc3066_language_tag ($value, sub { |
| 1072 |
$self->{onerror}->(@_, node => $attr); |
| 1073 |
}, $self->{level}); |
| 1074 |
} |
| 1075 |
## ISSUE: RFC 4646 (3066bis)? |
| 1076 |
|
| 1077 |
## TODO: test data |
| 1078 |
|
| 1079 |
## NOTE: Inconsistency between |lang| and |xml:lang| attributes are |
| 1080 |
## non-conforming. Such errors are detected by the checkers of |
| 1081 |
## |{}xml:lang| and |{xml}:lang| attributes. |
| 1082 |
}, |
| 1083 |
dir => $GetHTMLEnumeratedAttrChecker->({ltr => 1, rtl => 1}), |
| 1084 |
class => sub { |
| 1085 |
my ($self, $attr) = @_; |
| 1086 |
|
| 1087 |
## NOTE: "Unordered set of unique space-separated tokens". |
| 1088 |
|
| 1089 |
my %word; |
| 1090 |
for my $word (grep {length $_} |
| 1091 |
split /[\x09\x0A\x0C\x0D\x20]+/, $attr->value) { |
| 1092 |
unless ($word{$word}) { |
| 1093 |
$word{$word} = 1; |
| 1094 |
push @{$self->{return}->{class}->{$word}||=[]}, $attr; |
| 1095 |
} else { |
| 1096 |
$self->{onerror}->(node => $attr, type => 'duplicate token', |
| 1097 |
value => $word, |
| 1098 |
level => $self->{level}->{must}); |
| 1099 |
} |
| 1100 |
} |
| 1101 |
}, |
| 1102 |
contenteditable => $GetHTMLEnumeratedAttrChecker->({ |
| 1103 |
true => 1, false => 1, '' => 1, |
| 1104 |
}), |
| 1105 |
contextmenu => sub { |
| 1106 |
my ($self, $attr) = @_; |
| 1107 |
my $value = $attr->value; |
| 1108 |
push @{$self->{idref}}, ['menu', $value => $attr]; |
| 1109 |
## ISSUE: "The value must be the ID of a menu element in the DOM." |
| 1110 |
## What is "in the DOM"? A menu Element node that is not part |
| 1111 |
## of the Document tree is in the DOM? A menu Element node that |
| 1112 |
## belong to another Document tree is in the DOM? |
| 1113 |
}, |
| 1114 |
hidden => $GetHTMLBooleanAttrChecker->('hidden'), |
| 1115 |
irrelevant => $GetHTMLBooleanAttrChecker->('irrelevant'), |
| 1116 |
ref => $HTMLRefOrTemplateAttrChecker, |
| 1117 |
registrationmark => sub { |
| 1118 |
my ($self, $attr, $item, $element_state) = @_; |
| 1119 |
|
| 1120 |
## NOTE: Any value is conforming. |
| 1121 |
|
| 1122 |
if ($self->{flag}->{in_rule}) { |
| 1123 |
my $el = $attr->owner_element; |
| 1124 |
my $ln = $el->manakai_local_name; |
| 1125 |
if ($ln eq 'nest' or |
| 1126 |
($ln eq 'rule' and not $element_state->{in_rule_original})) { |
| 1127 |
my $nsuri = $el->namespace_uri; |
| 1128 |
if (defined $nsuri and $nsuri eq $HTML_NS) { |
| 1129 |
$self->{onerror}->(node => $attr, type => 'attribute not allowed', |
| 1130 |
level => $self->{level}->{must}); |
| 1131 |
} |
| 1132 |
} |
| 1133 |
} else { |
| 1134 |
$self->{onerror}->(node => $attr, type => 'attribute not allowed', |
| 1135 |
level => $self->{level}->{must}); |
| 1136 |
} |
| 1137 |
}, |
| 1138 |
repeat => sub { |
| 1139 |
my ($self, $attr) = @_; |
| 1140 |
|
| 1141 |
if (defined $attr->namespace_uri) { |
| 1142 |
my $oe = $attr->owner_element; |
| 1143 |
my $oe_nsuri = $oe->namespace_uri; |
| 1144 |
if (defined $oe_nsuri or $oe_nsuri eq $HTML_NS) { |
| 1145 |
$self->{onerror}->(node => $attr, type => 'attribute not allowed', |
| 1146 |
level => $self->{level}->{must}); |
| 1147 |
} |
| 1148 |
} |
| 1149 |
|
| 1150 |
my $value = $attr->value; |
| 1151 |
if ($value eq 'template') { |
| 1152 |
# |
| 1153 |
} elsif ($value =~ /\A-?[0-9]+\z/) { |
| 1154 |
# |
| 1155 |
} else { |
| 1156 |
$self->{onerror}->(node => $attr, type => 'repeat:syntax error', |
| 1157 |
level => $self->{level}->{must}); |
| 1158 |
} |
| 1159 |
|
| 1160 |
## ISSUE: "Repetition templates may occur anywhere." Does that mean |
| 1161 |
## that the attribute MAY be specified to any element, or that the |
| 1162 |
## element with that attribute (i.e. a repetition template) can be |
| 1163 |
## inserted anywhere in a document tree? |
| 1164 |
}, |
| 1165 |
'repeat-min' => $HTMLRepeatIndexAttrChecker, |
| 1166 |
'repeat-max' => $HTMLRepeatIndexAttrChecker, |
| 1167 |
'repeat-start' => $HTMLRepeatIndexAttrChecker, |
| 1168 |
'repeat-template' => sub { |
| 1169 |
my ($self, $attr) = @_; |
| 1170 |
|
| 1171 |
if (defined $attr->namespace_uri) { |
| 1172 |
my $oe = $attr->owner_element; |
| 1173 |
my $oe_nsuri = $oe->namespace_uri; |
| 1174 |
if (defined $oe_nsuri or $oe_nsuri eq $HTML_NS) { ## TODO: This condition is wrong? |
| 1175 |
$self->{onerror}->(node => $attr, type => 'attribute not allowed', |
| 1176 |
level => $self->{level}->{must}); |
| 1177 |
} |
| 1178 |
} |
| 1179 |
|
| 1180 |
## ISSUE: This attribute has no conformance requirement. |
| 1181 |
## ISSUE: Repetition blocks MAY have this attribute. Then, is the |
| 1182 |
## attribute allowed on an element that is not a repetition block? |
| 1183 |
}, |
| 1184 |
## TODO: role [HTML5ROLE] ## TODO: global @role [XHTML1ROLE] |
| 1185 |
style => sub { |
| 1186 |
my ($self, $attr) = @_; |
| 1187 |
|
| 1188 |
$self->{onsubdoc}->({s => $attr->value, |
| 1189 |
container_node => $attr, |
| 1190 |
media_type => 'text/x-css-inline', |
| 1191 |
is_char_string => 1}); |
| 1192 |
|
| 1193 |
## NOTE: "... MUST still be comprehensible and usable if those |
| 1194 |
## attributes were removed" is a semantic requirement, it cannot |
| 1195 |
## be tested. |
| 1196 |
}, |
| 1197 |
tabindex => $HTMLIntegerAttrChecker, |
| 1198 |
template => $HTMLRefOrTemplateAttrChecker, |
| 1199 |
'xml:lang' => sub { |
| 1200 |
my ($self, $attr) = @_; |
| 1201 |
|
| 1202 |
if ($attr->owner_document->manakai_is_html) { |
| 1203 |
$self->{onerror}->(type => 'in HTML:xml:lang', |
| 1204 |
level => $self->{level}->{info}, |
| 1205 |
node => $attr); |
| 1206 |
## NOTE: This is not an error, but the attribute will be ignored. |
| 1207 |
} else { |
| 1208 |
$self->{onerror}->(type => 'in XML:xml:lang', |
| 1209 |
level => $self->{level}->{html5_no_may}, |
| 1210 |
node => $attr); |
| 1211 |
## TODO: We need to add test for this error. |
| 1212 |
} |
| 1213 |
|
| 1214 |
my $lang_attr = $attr->owner_element->get_attribute_node_ns |
| 1215 |
(undef, 'lang'); |
| 1216 |
if ($lang_attr) { |
| 1217 |
my $lang_attr_value = $lang_attr->value; |
| 1218 |
$lang_attr_value =~ tr/A-Z/a-z/; ## ASCII case-insensitive |
| 1219 |
my $value = $attr->value; |
| 1220 |
$value =~ tr/A-Z/a-z/; ## ASCII case-insensitive |
| 1221 |
if ($lang_attr_value ne $value) { |
| 1222 |
$self->{onerror}->(type => 'xml:lang ne lang', |
| 1223 |
level => $self->{level}->{must}, |
| 1224 |
node => $attr); |
| 1225 |
} |
| 1226 |
} else { |
| 1227 |
$self->{onerror}->(type => 'xml:lang not allowed', |
| 1228 |
level => $self->{level}->{must}, |
| 1229 |
node => $attr); |
| 1230 |
## TODO: We need to add test for <x {xml}:lang {}xml:lang>. |
| 1231 |
} |
| 1232 |
}, |
| 1233 |
xmlns => sub { |
| 1234 |
my ($self, $attr) = @_; |
| 1235 |
my $value = $attr->value; |
| 1236 |
unless ($value eq $HTML_NS) { |
| 1237 |
$self->{onerror}->(node => $attr, type => 'invalid attribute value', |
| 1238 |
level => $self->{level}->{must}); |
| 1239 |
## TODO: Should be new "bad namespace" error? |
| 1240 |
} |
| 1241 |
unless ($attr->owner_document->manakai_is_html) { |
| 1242 |
$self->{onerror}->(node => $attr, type => 'in XML:xmlns', |
| 1243 |
level => $self->{level}->{must}); |
| 1244 |
## TODO: Test |
| 1245 |
} |
| 1246 |
|
| 1247 |
## TODO: Should be resolved? |
| 1248 |
push @{$self->{return}->{uri}->{$value} ||= []}, |
| 1249 |
{node => $attr, type => {namespace => 1}}; |
| 1250 |
}, |
| 1251 |
}; |
| 1252 |
|
| 1253 |
## ISSUE: Shouldn't the same-origin policy applied to the datatemplate feature? |
| 1254 |
|
| 1255 |
my %HTMLAttrStatus = ( |
| 1256 |
class => FEATURE_HTML5_DEFAULT, |
| 1257 |
contenteditable => FEATURE_HTML5_DEFAULT, |
| 1258 |
contextmenu => FEATURE_HTML5_WD, |
| 1259 |
dir => FEATURE_HTML5_DEFAULT, |
| 1260 |
draggable => FEATURE_HTML5_LC, |
| 1261 |
hidden => FEATURE_HTML5_DEFAULT, |
| 1262 |
id => FEATURE_HTML5_DEFAULT, |
| 1263 |
irrelevant => FEATURE_HTML5_DROPPED, |
| 1264 |
lang => FEATURE_HTML5_DEFAULT, |
| 1265 |
ref => FEATURE_HTML5_AT_RISK, |
| 1266 |
registrationmark => FEATURE_HTML5_AT_RISK, |
| 1267 |
repeat => FEATURE_WF2, |
| 1268 |
'repeat-max' => FEATURE_WF2, |
| 1269 |
'repeat-min' => FEATURE_WF2, |
| 1270 |
'repeat-start' => FEATURE_WF2, |
| 1271 |
'repeat-template' => FEATURE_WF2, |
| 1272 |
role => FEATURE_HTML5_ROLE, |
| 1273 |
style => FEATURE_HTML5_DEFAULT, |
| 1274 |
tabindex => FEATURE_HTML5_DEFAULT, |
| 1275 |
template => FEATURE_HTML5_AT_RISK, |
| 1276 |
title => FEATURE_HTML5_DEFAULT, |
| 1277 |
xmlns => FEATURE_HTML5_DEFAULT, |
| 1278 |
); |
| 1279 |
|
| 1280 |
my %HTMLM12NCommonAttrStatus = ( |
| 1281 |
about => FEATURE_RDFA_CR, |
| 1282 |
class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 1283 |
content => FEATURE_RDFA_CR, |
| 1284 |
datatype => FEATURE_RDFA_CR, |
| 1285 |
dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 1286 |
href => FEATURE_RDFA_CR, |
| 1287 |
id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 1288 |
instanceof => FEATURE_RDFA_LC, |
| 1289 |
onclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 1290 |
ondblclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 1291 |
onmousedown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 1292 |
onmouseup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 1293 |
onmouseover => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 1294 |
onmousemove => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 1295 |
onmouseout => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 1296 |
onkeypress => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 1297 |
onkeydown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 1298 |
onkeyup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 1299 |
property => FEATURE_RDFA_CR, |
| 1300 |
rel => FEATURE_RDFA_CR, |
| 1301 |
resource => FEATURE_RDFA_CR, |
| 1302 |
rev => FEATURE_RDFA_CR, |
| 1303 |
#style => FEATURE_HTML5_DEFAULT | FEATURE_XHTMLBASIC11_CR_DEPRECATED | |
| 1304 |
# FEATURE_M12N10_REC, |
| 1305 |
style => FEATURE_HTML5_DEFAULT | FEATURE_XHTMLBASIC11_CR | |
| 1306 |
FEATURE_M12N10_REC, |
| 1307 |
title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 1308 |
typeof => FEATURE_RDFA_CR, |
| 1309 |
); |
| 1310 |
|
| 1311 |
my %XHTML2CommonAttrStatus = ( |
| 1312 |
## Core |
| 1313 |
class => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED, |
| 1314 |
id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED, |
| 1315 |
#xml:id |
| 1316 |
layout => FEATURE_XHTML2_ED, |
| 1317 |
title => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED, |
| 1318 |
|
| 1319 |
## Hypertext |
| 1320 |
cite => FEATURE_XHTML2_ED, |
| 1321 |
href => FEATURE_XHTML2_ED, |
| 1322 |
hreflang => FEATURE_XHTML2_ED, |
| 1323 |
hrefmedia => FEATURE_XHTML2_ED, |
| 1324 |
hreftype => FEATURE_XHTML2_ED, |
| 1325 |
nextfocus => FEATURE_XHTML2_ED, |
| 1326 |
prevfocus => FEATURE_XHTML2_ED, |
| 1327 |
target => FEATURE_XHTML2_ED, |
| 1328 |
#xml:base |
| 1329 |
|
| 1330 |
## I18N |
| 1331 |
#xml:lang |
| 1332 |
|
| 1333 |
## Bi-directional |
| 1334 |
dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED, |
| 1335 |
|
| 1336 |
## Edit |
| 1337 |
edit => FEATURE_XHTML2_ED, |
| 1338 |
datetime => FEATURE_XHTML2_ED, |
| 1339 |
|
| 1340 |
## Embedding |
| 1341 |
encoding => FEATURE_XHTML2_ED, |
| 1342 |
src => FEATURE_XHTML2_ED, |
| 1343 |
srctype => FEATURE_XHTML2_ED, |
| 1344 |
|
| 1345 |
## Image Map |
| 1346 |
usemap => FEATURE_XHTML2_ED, |
| 1347 |
ismap => FEATURE_XHTML2_ED, |
| 1348 |
shape => FEATURE_XHTML2_ED, |
| 1349 |
coords => FEATURE_XHTML2_ED, |
| 1350 |
|
| 1351 |
## Media |
| 1352 |
media => FEATURE_XHTML2_ED, |
| 1353 |
|
| 1354 |
## Metadata |
| 1355 |
about => FEATURE_XHTML2_ED, |
| 1356 |
content => FEATURE_XHTML2_ED, |
| 1357 |
datatype => FEATURE_XHTML2_ED, |
| 1358 |
instanceof => FEATURE_XHTML2_ED, |
| 1359 |
property => FEATURE_XHTML2_ED, |
| 1360 |
rel => FEATURE_XHTML2_ED, |
| 1361 |
resource => FEATURE_XHTML2_ED, |
| 1362 |
rev => FEATURE_XHTML2_ED, |
| 1363 |
|
| 1364 |
## Role |
| 1365 |
role => FEATURE_HTML5_ROLE | FEATURE_XHTML2_ED, |
| 1366 |
|
| 1367 |
## Style |
| 1368 |
style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED, # "strongly discouraged" |
| 1369 |
); |
| 1370 |
|
| 1371 |
my %HTMLM12NXHTML2CommonAttrStatus = ( |
| 1372 |
%HTMLM12NCommonAttrStatus, |
| 1373 |
%XHTML2CommonAttrStatus, |
| 1374 |
|
| 1375 |
about => FEATURE_RDFA_CR | FEATURE_XHTML2_ED, |
| 1376 |
class => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 1377 |
content => FEATURE_RDFA_CR | FEATURE_XHTML2_ED, |
| 1378 |
datatype => FEATURE_RDFA_CR | FEATURE_XHTML2_ED, |
| 1379 |
dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 1380 |
href => FEATURE_RDFA_CR, |
| 1381 |
id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 1382 |
instanceof => FEATURE_RDFA_LC | FEATURE_XHTML2_ED, |
| 1383 |
property => FEATURE_RDFA_CR | FEATURE_XHTML2_ED, |
| 1384 |
rel => FEATURE_RDFA_CR | FEATURE_XHTML2_ED, |
| 1385 |
resource => FEATURE_RDFA_CR | FEATURE_XHTML2_ED, |
| 1386 |
rev => FEATURE_RDFA_CR | FEATURE_XHTML2_ED, |
| 1387 |
#style => FEATURE_HTML5_DEFAULT | FEATURE_XHTMLBASIC11_CR_DEPRECATED | |
| 1388 |
# FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 1389 |
style => FEATURE_HTML5_DEFAULT | FEATURE_XHTMLBASIC11_CR | |
| 1390 |
FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 1391 |
title => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 1392 |
typeof => FEATURE_RDFA_CR, |
| 1393 |
); |
| 1394 |
|
| 1395 |
for (qw/ |
| 1396 |
onabort onbeforeunload onblur onchange onclick oncontextmenu |
| 1397 |
ondblclick ondrag ondragend ondragenter ondragleave ondragover |
| 1398 |
ondragstart ondrop onerror onfocus onkeydown onkeypress |
| 1399 |
onkeyup onload onmessage onmousedown onmousemove onmouseout |
| 1400 |
onmouseover onmouseup onmousewheel onresize onscroll onselect |
| 1401 |
onstorage onsubmit onunload |
| 1402 |
/) { |
| 1403 |
$HTMLAttrChecker->{$_} = $HTMLEventHandlerAttrChecker; |
| 1404 |
$HTMLAttrStatus{$_} = FEATURE_HTML5_DEFAULT; |
| 1405 |
} |
| 1406 |
|
| 1407 |
## NOTE: Non-standard global attributes in the HTML namespace. |
| 1408 |
$AttrChecker->{$HTML_NS}->{''} = sub {}; # no syntactical checks |
| 1409 |
$AttrStatus->{$HTML_NS}->{''} = 0; # disallowed and not part of any standard |
| 1410 |
|
| 1411 |
$AttrStatus->{$HTML_NS}->{active} = FEATURE_HTML5_DROPPED; |
| 1412 |
for (qw/repeat repeat-max repeat-min repeat-start repeat-template/) { |
| 1413 |
$AttrChecker->{$HTML_NS}->{$_} = $HTMLAttrChecker->{$_}; |
| 1414 |
$AttrStatus->{$HTML_NS}->{$_} = FEATURE_WF2; |
| 1415 |
} |
| 1416 |
|
| 1417 |
for (qw/about content datatype property rel resource rev/) { |
| 1418 |
$AttrStatus->{$HTML_NS}->{$_} = FEATURE_RDFA_CR | FEATURE_XHTML2_ED; |
| 1419 |
} |
| 1420 |
$AttrStatus->{$HTML_NS}->{instanceof} = FEATURE_RDFA_LC | FEATURE_XHTML2_ED; |
| 1421 |
$AttrStatus->{$HTML_NS}->{typeof} = FEATURE_RDFA_CR; |
| 1422 |
$AttrStatus->{$HTML_NS}->{role} = FEATURE_ROLE_LC; |
| 1423 |
for (qw/cite coords datetime edit encoding href hreflang hrefmedia hreftype |
| 1424 |
ismap layout media nextfocus prevfocus shape src srctype style |
| 1425 |
target usemap/) { |
| 1426 |
$AttrStatus->{$HTML_NS}->{$_} = FEATURE_XHTML2_ED; |
| 1427 |
} |
| 1428 |
for (qw/class dir id title/) { |
| 1429 |
$AttrStatus->{$HTML_NS}->{$_} = FEATURE_M12N11_LC | FEATURE_XHTML2_ED; |
| 1430 |
} |
| 1431 |
for (qw/onclick ondblclick onmousedown onmouseup onmouseover onmousemove |
| 1432 |
onmouseout onkeypress onkeydown onkeyup/) { |
| 1433 |
$AttrStatus->{$HTML_NS}->{$_} = FEATURE_M12N11_LC; |
| 1434 |
} |
| 1435 |
|
| 1436 |
my $HTMLDatasetAttrChecker = sub { |
| 1437 |
## NOTE: "Authors should ... when the attributes are ignored and |
| 1438 |
## any associated CSS dropped, the page is still usable." (semantic |
| 1439 |
## constraint.) |
| 1440 |
}; # $HTMLDatasetAttrChecker |
| 1441 |
|
| 1442 |
my $HTMLDatasetAttrStatus = FEATURE_HTML5_DEFAULT; |
| 1443 |
|
| 1444 |
my $GetHTMLAttrsChecker = sub { |
| 1445 |
my $element_specific_checker = shift; |
| 1446 |
my $element_specific_status = shift; |
| 1447 |
return sub { |
| 1448 |
my ($self, $item, $element_state) = @_; |
| 1449 |
for my $attr (@{$item->{node}->attributes}) { |
| 1450 |
my $attr_ns = $attr->namespace_uri; |
| 1451 |
$attr_ns = '' unless defined $attr_ns; |
| 1452 |
my $attr_ln = $attr->manakai_local_name; |
| 1453 |
my $checker; |
| 1454 |
my $status; |
| 1455 |
if ($attr_ns eq '') { |
| 1456 |
if ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and |
| 1457 |
$attr_ln !~ /[A-Z]/) { |
| 1458 |
$checker = $HTMLDatasetAttrChecker; |
| 1459 |
$status = $HTMLDatasetAttrStatus; |
| 1460 |
} else { |
| 1461 |
$checker = $element_specific_checker->{$attr_ln} |
| 1462 |
|| $HTMLAttrChecker->{$attr_ln}; |
| 1463 |
$status = $element_specific_status->{$attr_ln}; |
| 1464 |
} |
| 1465 |
} |
| 1466 |
$checker ||= $AttrChecker->{$attr_ns}->{$attr_ln} |
| 1467 |
|| $AttrChecker->{$attr_ns}->{''}; |
| 1468 |
$status ||= $AttrStatus->{$attr_ns}->{$attr_ln} |
| 1469 |
|| $AttrStatus->{$attr_ns}->{''}; |
| 1470 |
$status = FEATURE_ALLOWED if not defined $status and length $attr_ns; |
| 1471 |
if ($checker) { |
| 1472 |
$checker->($self, $attr, $item, $element_state); |
| 1473 |
} elsif ($attr_ns eq '' and not $element_specific_status->{$attr_ln}) { |
| 1474 |
# |
| 1475 |
} else { |
| 1476 |
$self->{onerror}->(node => $attr, |
| 1477 |
type => 'unknown attribute', |
| 1478 |
level => $self->{level}->{uncertain}); |
| 1479 |
## ISSUE: No conformance createria for unknown attributes in the spec |
| 1480 |
} |
| 1481 |
$self->_attr_status_info ($attr, $status); |
| 1482 |
} |
| 1483 |
}; |
| 1484 |
}; # $GetHTMLAttrsChecker |
| 1485 |
|
| 1486 |
my %HTMLChecker = ( |
| 1487 |
%Whatpm::ContentChecker::AnyChecker, |
| 1488 |
check_start => sub { |
| 1489 |
my ($self, $item, $element_state) = @_; |
| 1490 |
|
| 1491 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 1492 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 1493 |
}, |
| 1494 |
check_attrs => $GetHTMLAttrsChecker->({}, \%HTMLAttrStatus), |
| 1495 |
); |
| 1496 |
|
| 1497 |
my %HTMLEmptyChecker = ( |
| 1498 |
%HTMLChecker, |
| 1499 |
check_child_element => sub { |
| 1500 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 1501 |
$child_is_transparent, $element_state) = @_; |
| 1502 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 1503 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 1504 |
$self->{onerror}->(node => $child_el, |
| 1505 |
type => 'element not allowed:minus', |
| 1506 |
level => $self->{level}->{must}); |
| 1507 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 1508 |
# |
| 1509 |
} else { |
| 1510 |
$self->{onerror}->(node => $child_el, |
| 1511 |
type => 'element not allowed:empty', |
| 1512 |
level => $self->{level}->{must}); |
| 1513 |
} |
| 1514 |
}, |
| 1515 |
check_child_text => sub { |
| 1516 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 1517 |
if ($has_significant) { |
| 1518 |
$self->{onerror}->(node => $child_node, |
| 1519 |
type => 'character not allowed:empty', |
| 1520 |
level => $self->{level}->{must}); |
| 1521 |
} |
| 1522 |
}, |
| 1523 |
); |
| 1524 |
|
| 1525 |
my %HTMLTextChecker = ( |
| 1526 |
%HTMLChecker, |
| 1527 |
check_child_element => sub { |
| 1528 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 1529 |
$child_is_transparent, $element_state) = @_; |
| 1530 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 1531 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 1532 |
$self->{onerror}->(node => $child_el, |
| 1533 |
type => 'element not allowed:minus', |
| 1534 |
level => $self->{level}->{must}); |
| 1535 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 1536 |
# |
| 1537 |
} else { |
| 1538 |
$self->{onerror}->(node => $child_el, type => 'element not allowed:text', |
| 1539 |
level => $self->{level}->{must}); |
| 1540 |
} |
| 1541 |
}, |
| 1542 |
); |
| 1543 |
|
| 1544 |
my %HTMLFlowContentChecker = ( |
| 1545 |
%HTMLChecker, |
| 1546 |
check_child_element => sub { |
| 1547 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 1548 |
$child_is_transparent, $element_state) = @_; |
| 1549 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 1550 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 1551 |
$self->{onerror}->(node => $child_el, |
| 1552 |
type => 'element not allowed:minus', |
| 1553 |
level => $self->{level}->{must}); |
| 1554 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 1555 |
# |
| 1556 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') { |
| 1557 |
if ($element_state->{has_non_style} or |
| 1558 |
not $child_el->has_attribute_ns (undef, 'scoped')) { |
| 1559 |
$self->{onerror}->(node => $child_el, |
| 1560 |
type => 'element not allowed:flow style', |
| 1561 |
level => $self->{level}->{must}); |
| 1562 |
} |
| 1563 |
} elsif ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) { |
| 1564 |
$element_state->{has_non_style} = 1 unless $child_is_transparent; |
| 1565 |
} else { |
| 1566 |
$element_state->{has_non_style} = 1; |
| 1567 |
$self->{onerror}->(node => $child_el, |
| 1568 |
type => 'element not allowed:flow', |
| 1569 |
level => $self->{level}->{must}) |
| 1570 |
} |
| 1571 |
}, |
| 1572 |
check_child_text => sub { |
| 1573 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 1574 |
if ($has_significant) { |
| 1575 |
$element_state->{has_non_style} = 1; |
| 1576 |
} |
| 1577 |
}, |
| 1578 |
check_end => sub { |
| 1579 |
my ($self, $item, $element_state) = @_; |
| 1580 |
## NOTE: A modified copy of the code below is in |datagrid| checker. |
| 1581 |
if ($element_state->{has_significant}) { |
| 1582 |
$item->{real_parent_state}->{has_significant} = 1; |
| 1583 |
} elsif ($item->{transparent}) { |
| 1584 |
# |
| 1585 |
} else { |
| 1586 |
$self->{onerror}->(node => $item->{node}, |
| 1587 |
level => $self->{level}->{should}, |
| 1588 |
type => 'no significant content'); |
| 1589 |
} |
| 1590 |
}, |
| 1591 |
); |
| 1592 |
|
| 1593 |
my %HTMLPhrasingContentChecker = ( |
| 1594 |
%HTMLChecker, |
| 1595 |
check_child_element => sub { |
| 1596 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 1597 |
$child_is_transparent, $element_state) = @_; |
| 1598 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 1599 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 1600 |
$self->{onerror}->(node => $child_el, |
| 1601 |
type => 'element not allowed:minus', |
| 1602 |
level => $self->{level}->{must}); |
| 1603 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 1604 |
# |
| 1605 |
} elsif ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) { |
| 1606 |
# |
| 1607 |
} else { |
| 1608 |
$self->{onerror}->(node => $child_el, |
| 1609 |
type => 'element not allowed:phrasing', |
| 1610 |
level => $self->{level}->{must}); |
| 1611 |
} |
| 1612 |
}, |
| 1613 |
check_end => $HTMLFlowContentChecker{check_end}, |
| 1614 |
## NOTE: The definition for |li| assumes that the only differences |
| 1615 |
## between flow and phrasing content checkers are |check_child_element| |
| 1616 |
## and |check_child_text|. |
| 1617 |
); |
| 1618 |
|
| 1619 |
my %HTMLTransparentChecker = %HTMLFlowContentChecker; |
| 1620 |
## ISSUE: Significant content rule should be applied to transparent element |
| 1621 |
## with parent? |
| 1622 |
|
| 1623 |
our $Element; |
| 1624 |
our $ElementDefault; |
| 1625 |
|
| 1626 |
$Element->{$HTML_NS}->{''} = { |
| 1627 |
%HTMLChecker, |
| 1628 |
}; |
| 1629 |
|
| 1630 |
$Element->{$HTML_NS}->{html} = { |
| 1631 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 1632 |
is_root => 1, |
| 1633 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 1634 |
manifest => $HTMLURIAttrChecker, |
| 1635 |
version => sub { |
| 1636 |
## NOTE: According to HTML4 prose, this is a "cdata" attribute. |
| 1637 |
## Though DTDs of various versions of HTML define the attribute |
| 1638 |
## as |#FIXED|, this conformance checker does no check for |
| 1639 |
## the attribute value, since what kind of check should be done |
| 1640 |
## is unknown. |
| 1641 |
}, |
| 1642 |
}, { |
| 1643 |
%HTMLAttrStatus, |
| 1644 |
%XHTML2CommonAttrStatus, |
| 1645 |
class => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_HTML2X_RFC, |
| 1646 |
dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 1647 |
id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC, |
| 1648 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 1649 |
manifest => FEATURE_HTML5_DEFAULT, |
| 1650 |
sdaform => FEATURE_HTML20_RFC, |
| 1651 |
version => FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 1652 |
}), |
| 1653 |
check_start => sub { |
| 1654 |
my ($self, $item, $element_state) = @_; |
| 1655 |
$element_state->{phase} = 'before head'; |
| 1656 |
|
| 1657 |
$element_state->{uri_info}->{manifest}->{type}->{resource} = 1; |
| 1658 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 1659 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 1660 |
}, |
| 1661 |
check_child_element => sub { |
| 1662 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 1663 |
$child_is_transparent, $element_state) = @_; |
| 1664 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 1665 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 1666 |
$self->{onerror}->(node => $child_el, |
| 1667 |
type => 'element not allowed:minus', |
| 1668 |
level => $self->{level}->{must}); |
| 1669 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 1670 |
# |
| 1671 |
} elsif ($element_state->{phase} eq 'before head') { |
| 1672 |
if ($child_nsuri eq $HTML_NS and $child_ln eq 'head') { |
| 1673 |
$element_state->{phase} = 'after head'; |
| 1674 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'body') { |
| 1675 |
$self->{onerror}->(node => $child_el, |
| 1676 |
type => 'ps element missing', |
| 1677 |
text => 'head', |
| 1678 |
level => $self->{level}->{must}); |
| 1679 |
$element_state->{phase} = 'after body'; |
| 1680 |
} else { |
| 1681 |
$self->{onerror}->(node => $child_el, |
| 1682 |
type => 'element not allowed', |
| 1683 |
level => $self->{level}->{must}); |
| 1684 |
} |
| 1685 |
} elsif ($element_state->{phase} eq 'after head') { |
| 1686 |
if ($child_nsuri eq $HTML_NS and $child_ln eq 'body') { |
| 1687 |
$element_state->{phase} = 'after body'; |
| 1688 |
} else { |
| 1689 |
$self->{onerror}->(node => $child_el, |
| 1690 |
type => 'element not allowed', |
| 1691 |
level => $self->{level}->{must}); |
| 1692 |
} |
| 1693 |
} elsif ($element_state->{phase} eq 'after body') { |
| 1694 |
$self->{onerror}->(node => $child_el, |
| 1695 |
type => 'element not allowed', |
| 1696 |
level => $self->{level}->{must}); |
| 1697 |
} else { |
| 1698 |
die "check_child_element: Bad |html| phase: $element_state->{phase}"; |
| 1699 |
} |
| 1700 |
}, |
| 1701 |
check_child_text => sub { |
| 1702 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 1703 |
if ($has_significant) { |
| 1704 |
$self->{onerror}->(node => $child_node, |
| 1705 |
type => 'character not allowed', |
| 1706 |
level => $self->{level}->{must}); |
| 1707 |
} |
| 1708 |
}, |
| 1709 |
check_end => sub { |
| 1710 |
my ($self, $item, $element_state) = @_; |
| 1711 |
if ($element_state->{phase} eq 'after body') { |
| 1712 |
# |
| 1713 |
} elsif ($element_state->{phase} eq 'before head') { |
| 1714 |
$self->{onerror}->(node => $item->{node}, |
| 1715 |
type => 'child element missing', |
| 1716 |
text => 'head', |
| 1717 |
level => $self->{level}->{must}); |
| 1718 |
$self->{onerror}->(node => $item->{node}, |
| 1719 |
type => 'child element missing', |
| 1720 |
text => 'body', |
| 1721 |
level => $self->{level}->{must}); |
| 1722 |
} elsif ($element_state->{phase} eq 'after head') { |
| 1723 |
$self->{onerror}->(node => $item->{node}, |
| 1724 |
type => 'child element missing', |
| 1725 |
text => 'body', |
| 1726 |
level => $self->{level}->{must}); |
| 1727 |
} else { |
| 1728 |
die "check_end: Bad |html| phase: $element_state->{phase}"; |
| 1729 |
} |
| 1730 |
|
| 1731 |
$HTMLChecker{check_end}->(@_); |
| 1732 |
}, |
| 1733 |
}; |
| 1734 |
|
| 1735 |
$Element->{$HTML_NS}->{head} = { |
| 1736 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 1737 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 1738 |
profile => $HTMLSpaceURIsAttrChecker, ## NOTE: MUST be profile URIs. |
| 1739 |
}, { |
| 1740 |
%HTMLAttrStatus, |
| 1741 |
%XHTML2CommonAttrStatus, |
| 1742 |
class => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_HTML2X_RFC, |
| 1743 |
dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 1744 |
id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC, |
| 1745 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 1746 |
profile => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC, |
| 1747 |
}), |
| 1748 |
check_child_element => sub { |
| 1749 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 1750 |
$child_is_transparent, $element_state) = @_; |
| 1751 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 1752 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 1753 |
$self->{onerror}->(node => $child_el, |
| 1754 |
type => 'element not allowed:minus', |
| 1755 |
level => $self->{level}->{must}); |
| 1756 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 1757 |
# |
| 1758 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'title') { |
| 1759 |
unless ($element_state->{has_title}) { |
| 1760 |
$element_state->{has_title} = 1; |
| 1761 |
} else { |
| 1762 |
$self->{onerror}->(node => $child_el, |
| 1763 |
type => 'element not allowed:head title', |
| 1764 |
level => $self->{level}->{must}); |
| 1765 |
} |
| 1766 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') { |
| 1767 |
if ($child_el->has_attribute_ns (undef, 'scoped')) { |
| 1768 |
$self->{onerror}->(node => $child_el, |
| 1769 |
type => 'element not allowed:head style', |
| 1770 |
level => $self->{level}->{must}); |
| 1771 |
} |
| 1772 |
} elsif ($HTMLMetadataContent->{$child_nsuri}->{$child_ln}) { |
| 1773 |
# |
| 1774 |
|
| 1775 |
## NOTE: |meta| is a metadata content. However, strictly speaking, |
| 1776 |
## a |meta| element with none of |charset|, |name|, |
| 1777 |
## or |http-equiv| attribute is not allowed. It is non-conforming |
| 1778 |
## anyway. |
| 1779 |
|
| 1780 |
## TODO: |form| MUST be empty and in XML [WF2]. |
| 1781 |
} else { |
| 1782 |
$self->{onerror}->(node => $child_el, |
| 1783 |
type => 'element not allowed:metadata', |
| 1784 |
level => $self->{level}->{must}); |
| 1785 |
} |
| 1786 |
$element_state->{in_head_original} = $self->{flag}->{in_head}; |
| 1787 |
$self->{flag}->{in_head} = 1; |
| 1788 |
}, |
| 1789 |
check_child_text => sub { |
| 1790 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 1791 |
if ($has_significant) { |
| 1792 |
$self->{onerror}->(node => $child_node, type => 'character not allowed', |
| 1793 |
level => $self->{level}->{must}); |
| 1794 |
} |
| 1795 |
}, |
| 1796 |
check_end => sub { |
| 1797 |
my ($self, $item, $element_state) = @_; |
| 1798 |
unless ($element_state->{has_title}) { |
| 1799 |
$self->{onerror}->(node => $item->{node}, |
| 1800 |
type => 'child element missing', |
| 1801 |
text => 'title', |
| 1802 |
level => $self->{level}->{must}); |
| 1803 |
} |
| 1804 |
$self->{flag}->{in_head} = $element_state->{in_head_original}; |
| 1805 |
|
| 1806 |
$HTMLChecker{check_end}->(@_); |
| 1807 |
}, |
| 1808 |
}; |
| 1809 |
|
| 1810 |
$Element->{$HTML_NS}->{title} = { |
| 1811 |
%HTMLTextChecker, |
| 1812 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 1813 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 1814 |
%HTMLAttrStatus, |
| 1815 |
%XHTML2CommonAttrStatus, |
| 1816 |
class => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_HTML2X_RFC, |
| 1817 |
dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 1818 |
id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC, |
| 1819 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 1820 |
sdaform => FEATURE_HTML20_RFC, |
| 1821 |
}), |
| 1822 |
}; |
| 1823 |
|
| 1824 |
$Element->{$HTML_NS}->{base} = { |
| 1825 |
status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 1826 |
%HTMLEmptyChecker, |
| 1827 |
check_attrs => sub { |
| 1828 |
my ($self, $item, $element_state) = @_; |
| 1829 |
|
| 1830 |
if ($self->{has_base}) { |
| 1831 |
$self->{onerror}->(node => $item->{node}, |
| 1832 |
type => 'element not allowed:base', |
| 1833 |
level => $self->{level}->{must}); |
| 1834 |
} else { |
| 1835 |
$self->{has_base} = 1; |
| 1836 |
} |
| 1837 |
|
| 1838 |
my $has_href = $item->{node}->has_attribute_ns (undef, 'href'); |
| 1839 |
my $has_target = $item->{node}->has_attribute_ns (undef, 'target'); |
| 1840 |
|
| 1841 |
if ($self->{has_uri_attr} and $has_href) { |
| 1842 |
## ISSUE: Are these examples conforming? |
| 1843 |
## <head profile="a b c"><base href> (except for |profile|'s |
| 1844 |
## non-conformance) |
| 1845 |
## <title xml:base="relative"/><base href/> (maybe it should be) |
| 1846 |
## <unknown xmlns="relative"/><base href/> (assuming that |
| 1847 |
## |{relative}:unknown| is allowed before XHTML |base| (unlikely, though)) |
| 1848 |
## <style>@import 'relative';</style><base href> |
| 1849 |
## <script>location.href = 'relative';</script><base href> |
| 1850 |
## NOTE: <html manifest=".."><head><base href=""/> is conforming as |
| 1851 |
## an exception. |
| 1852 |
$self->{onerror}->(node => $item->{node}, |
| 1853 |
type => 'basehref after URL attribute', |
| 1854 |
level => $self->{level}->{must}); |
| 1855 |
} |
| 1856 |
if ($self->{has_hyperlink_element} and $has_target) { |
| 1857 |
## ISSUE: Are these examples conforming? |
| 1858 |
## <head><title xlink:href=""/><base target="name"/></head> |
| 1859 |
## <xbl:xbl>...<svg:a href=""/>...</xbl:xbl><base target="name"/> |
| 1860 |
## (assuming that |xbl:xbl| is allowed before |base|) |
| 1861 |
## NOTE: These are non-conformant anyway because of |head|'s content model: |
| 1862 |
## <link href=""/><base target="name"/> |
| 1863 |
## <link rel=unknown href=""><base target=name> |
| 1864 |
$self->{onerror}->(node => $item->{node}, |
| 1865 |
type => 'basetarget after hyperlink', |
| 1866 |
level => $self->{level}->{must}); |
| 1867 |
} |
| 1868 |
|
| 1869 |
if (not $has_href and not $has_target) { |
| 1870 |
$self->{onerror}->(node => $item->{node}, |
| 1871 |
type => 'attribute missing:href|target', |
| 1872 |
level => $self->{level}->{must}); |
| 1873 |
} |
| 1874 |
|
| 1875 |
$element_state->{uri_info}->{href}->{type}->{base} = 1; |
| 1876 |
|
| 1877 |
return $GetHTMLAttrsChecker->({ |
| 1878 |
href => $HTMLURIAttrChecker, |
| 1879 |
target => $HTMLTargetAttrChecker, |
| 1880 |
}, { |
| 1881 |
%HTMLAttrStatus, |
| 1882 |
href => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 1883 |
id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 1884 |
target => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 1885 |
})->($self, $item, $element_state); |
| 1886 |
}, |
| 1887 |
}; |
| 1888 |
|
| 1889 |
$Element->{$HTML_NS}->{link} = { |
| 1890 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 1891 |
%HTMLEmptyChecker, |
| 1892 |
check_attrs => sub { |
| 1893 |
my ($self, $item, $element_state) = @_; |
| 1894 |
my $sizes_attr; |
| 1895 |
$GetHTMLAttrsChecker->({ |
| 1896 |
charset => sub { |
| 1897 |
my ($self, $attr) = @_; |
| 1898 |
$HTMLCharsetChecker->($attr->value, @_); |
| 1899 |
}, |
| 1900 |
href => $HTMLURIAttrChecker, |
| 1901 |
rel => sub { $HTMLLinkTypesAttrChecker->(0, $item, @_) }, |
| 1902 |
rev => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(), |
| 1903 |
media => $HTMLMQAttrChecker, |
| 1904 |
hreflang => $HTMLLanguageTagAttrChecker, |
| 1905 |
sizes => sub { |
| 1906 |
my ($self, $attr) = @_; |
| 1907 |
$sizes_attr = $attr; |
| 1908 |
my %word; |
| 1909 |
for my $word (grep {length $_} |
| 1910 |
split /[\x09\x0A\x0C\x0D\x20]+/, $attr->value) { |
| 1911 |
unless ($word{$word}) { |
| 1912 |
$word{$word} = 1; |
| 1913 |
if ($word eq 'any' or $word =~ /\A[1-9][0-9]*x[1-9][0-9]*\z/) { |
| 1914 |
# |
| 1915 |
} else { |
| 1916 |
$self->{onerror}->(node => $attr, |
| 1917 |
type => 'sizes:syntax error', |
| 1918 |
value => $word, |
| 1919 |
level => $self->{level}->{must}); |
| 1920 |
} |
| 1921 |
} else { |
| 1922 |
$self->{onerror}->(node => $attr, type => 'duplicate token', |
| 1923 |
value => $word, |
| 1924 |
level => $self->{level}->{must}); |
| 1925 |
} |
| 1926 |
} |
| 1927 |
}, |
| 1928 |
target => $HTMLTargetAttrChecker, |
| 1929 |
type => $HTMLIMTAttrChecker, |
| 1930 |
## NOTE: Though |title| has special semantics, |
| 1931 |
## syntactically same as the |title| as global attribute. |
| 1932 |
}, { |
| 1933 |
%HTMLAttrStatus, |
| 1934 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 1935 |
charset => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC, |
| 1936 |
## NOTE: |charset| attribute had been part of HTML5 spec though |
| 1937 |
## it had been commented out. |
| 1938 |
href => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_CR | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 1939 |
hreflang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | |
| 1940 |
FEATURE_M12N10_REC, |
| 1941 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 1942 |
media => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 1943 |
methods => FEATURE_HTML20_RFC, |
| 1944 |
rel => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_CR | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 1945 |
rev => FEATURE_RDFA_CR | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 1946 |
sdapref => FEATURE_HTML20_RFC, |
| 1947 |
sizes => FEATURE_HTML5_DEFAULT, |
| 1948 |
target => FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 1949 |
type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 1950 |
urn => FEATURE_HTML20_RFC, |
| 1951 |
})->($self, $item, $element_state); |
| 1952 |
|
| 1953 |
if ($item->{node}->has_attribute_ns (undef, 'href')) { |
| 1954 |
$self->{has_hyperlink_element} = 1 if $item->{has_hyperlink_link_type}; |
| 1955 |
} else { |
| 1956 |
$self->{onerror}->(node => $item->{node}, |
| 1957 |
type => 'attribute missing', |
| 1958 |
text => 'href', |
| 1959 |
level => $self->{level}->{must}); |
| 1960 |
} |
| 1961 |
|
| 1962 |
unless ($item->{node}->has_attribute_ns (undef, 'rel')) { |
| 1963 |
$self->{onerror}->(node => $item->{node}, |
| 1964 |
type => 'attribute missing', |
| 1965 |
text => 'rel', |
| 1966 |
level => $self->{level}->{must}); |
| 1967 |
} |
| 1968 |
|
| 1969 |
if ($sizes_attr and not $element_state->{link_rel}->{icon}) { |
| 1970 |
$self->{onerror}->(node => $sizes_attr, |
| 1971 |
type => 'attribute not allowed', |
| 1972 |
level => $self->{level}->{must}); |
| 1973 |
} |
| 1974 |
|
| 1975 |
if ($element_state->{link_rel}->{alternate} and |
| 1976 |
$element_state->{link_rel}->{stylesheet}) { |
| 1977 |
my $title_attr = $item->{node}->get_attribute_node_ns (undef, 'title'); |
| 1978 |
unless ($title_attr) { |
| 1979 |
$self->{onerror}->(node => $item->{node}, |
| 1980 |
type => 'attribute missing', |
| 1981 |
text => 'title', |
| 1982 |
level => $self->{level}->{must}); |
| 1983 |
} elsif ($title_attr->value eq '') { |
| 1984 |
$self->{onerror}->(node => $title_attr, |
| 1985 |
type => 'empty style sheet title', |
| 1986 |
level => $self->{level}->{must}); |
| 1987 |
} |
| 1988 |
} |
| 1989 |
}, |
| 1990 |
}; |
| 1991 |
|
| 1992 |
$Element->{$HTML_NS}->{meta} = { |
| 1993 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 1994 |
%HTMLEmptyChecker, |
| 1995 |
check_attrs => sub { |
| 1996 |
my ($self, $item, $element_state) = @_; |
| 1997 |
my $name_attr; |
| 1998 |
my $http_equiv_attr; |
| 1999 |
my $charset_attr; |
| 2000 |
my $content_attr; |
| 2001 |
for my $attr (@{$item->{node}->attributes}) { |
| 2002 |
my $attr_ns = $attr->namespace_uri; |
| 2003 |
$attr_ns = '' unless defined $attr_ns; |
| 2004 |
my $attr_ln = $attr->manakai_local_name; |
| 2005 |
my $checker; |
| 2006 |
my $status; |
| 2007 |
if ($attr_ns eq '') { |
| 2008 |
$status = { |
| 2009 |
%HTMLAttrStatus, |
| 2010 |
%XHTML2CommonAttrStatus, |
| 2011 |
charset => FEATURE_HTML5_DEFAULT, |
| 2012 |
content => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | |
| 2013 |
FEATURE_M12N10_REC, |
| 2014 |
dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | |
| 2015 |
FEATURE_M12N10_REC, |
| 2016 |
'http-equiv' => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 2017 |
id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | |
| 2018 |
FEATURE_XHTML10_REC, |
| 2019 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 2020 |
name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 2021 |
scheme => FEATURE_M12N10_REC, |
| 2022 |
}->{$attr_ln}; |
| 2023 |
|
| 2024 |
if ($attr_ln eq 'content') { |
| 2025 |
$content_attr = $attr; |
| 2026 |
$checker = 1; |
| 2027 |
} elsif ($attr_ln eq 'name') { |
| 2028 |
$name_attr = $attr; |
| 2029 |
$checker = 1; |
| 2030 |
} elsif ($attr_ln eq 'http-equiv') { |
| 2031 |
$http_equiv_attr = $attr; |
| 2032 |
$checker = 1; |
| 2033 |
} elsif ($attr_ln eq 'charset') { |
| 2034 |
$charset_attr = $attr; |
| 2035 |
$checker = 1; |
| 2036 |
} elsif ($attr_ln eq 'scheme') { |
| 2037 |
## NOTE: <http://suika.fam.cx/2007/html/standards#html-meta-scheme> |
| 2038 |
$checker = sub {}; |
| 2039 |
} elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and |
| 2040 |
$attr_ln !~ /[A-Z]/) { |
| 2041 |
$checker = $HTMLDatasetAttrChecker; |
| 2042 |
$status = $HTMLDatasetAttrStatus; |
| 2043 |
} else { |
| 2044 |
$checker = $HTMLAttrChecker->{$attr_ln} |
| 2045 |
|| $AttrChecker->{$attr_ns}->{$attr_ln} |
| 2046 |
|| $AttrChecker->{$attr_ns}->{''}; |
| 2047 |
} |
| 2048 |
} else { |
| 2049 |
$checker ||= $AttrChecker->{$attr_ns}->{$attr_ln} |
| 2050 |
|| $AttrChecker->{$attr_ns}->{''}; |
| 2051 |
$status ||= $AttrStatus->{$attr_ns}->{$attr_ln} |
| 2052 |
|| $AttrStatus->{$attr_ns}->{''}; |
| 2053 |
$status = FEATURE_ALLOWED if not defined $status; |
| 2054 |
} |
| 2055 |
|
| 2056 |
if ($checker) { |
| 2057 |
$checker->($self, $attr, $item, $element_state) if ref $checker; |
| 2058 |
} elsif ($attr_ns eq '' and not $status) { |
| 2059 |
# |
| 2060 |
} else { |
| 2061 |
$self->{onerror}->(node => $attr, |
| 2062 |
type => 'unknown attribute', |
| 2063 |
level => $self->{level}->{uncertain}); |
| 2064 |
## ISSUE: No conformance createria for unknown attributes in the spec |
| 2065 |
} |
| 2066 |
|
| 2067 |
$self->_attr_status_info ($attr, $status); |
| 2068 |
} |
| 2069 |
|
| 2070 |
if (defined $name_attr) { |
| 2071 |
if (defined $http_equiv_attr) { |
| 2072 |
$self->{onerror}->(node => $http_equiv_attr, |
| 2073 |
type => 'attribute not allowed', |
| 2074 |
level => $self->{level}->{must}); |
| 2075 |
} elsif (defined $charset_attr) { |
| 2076 |
$self->{onerror}->(node => $charset_attr, |
| 2077 |
type => 'attribute not allowed', |
| 2078 |
level => $self->{level}->{must}); |
| 2079 |
} |
| 2080 |
my $metadata_name = $name_attr->value; |
| 2081 |
my $metadata_value; |
| 2082 |
if (defined $content_attr) { |
| 2083 |
$metadata_value = $content_attr->value; |
| 2084 |
} else { |
| 2085 |
$self->{onerror}->(node => $item->{node}, |
| 2086 |
type => 'attribute missing', |
| 2087 |
text => 'content', |
| 2088 |
level => $self->{level}->{must}); |
| 2089 |
$metadata_value = ''; |
| 2090 |
} |
| 2091 |
} elsif (defined $http_equiv_attr) { |
| 2092 |
if (defined $charset_attr) { |
| 2093 |
$self->{onerror}->(node => $charset_attr, |
| 2094 |
type => 'attribute not allowed', |
| 2095 |
level => $self->{level}->{must}); |
| 2096 |
} |
| 2097 |
unless (defined $content_attr) { |
| 2098 |
$self->{onerror}->(node => $item->{node}, |
| 2099 |
type => 'attribute missing', |
| 2100 |
text => 'content', |
| 2101 |
level => $self->{level}->{must}); |
| 2102 |
} |
| 2103 |
} elsif (defined $charset_attr) { |
| 2104 |
if (defined $content_attr) { |
| 2105 |
$self->{onerror}->(node => $content_attr, |
| 2106 |
type => 'attribute not allowed', |
| 2107 |
level => $self->{level}->{must}); |
| 2108 |
} |
| 2109 |
} else { |
| 2110 |
if (defined $content_attr) { |
| 2111 |
$self->{onerror}->(node => $content_attr, |
| 2112 |
type => 'attribute not allowed', |
| 2113 |
level => $self->{level}->{must}); |
| 2114 |
$self->{onerror}->(node => $item->{node}, |
| 2115 |
type => 'attribute missing:name|http-equiv', |
| 2116 |
level => $self->{level}->{must}); |
| 2117 |
} else { |
| 2118 |
$self->{onerror}->(node => $item->{node}, |
| 2119 |
type => 'attribute missing:name|http-equiv|charset', |
| 2120 |
level => $self->{level}->{must}); |
| 2121 |
} |
| 2122 |
} |
| 2123 |
|
| 2124 |
my $check_charset_decl = sub () { |
| 2125 |
my $parent = $item->{node}->manakai_parent_element; |
| 2126 |
if ($parent and $parent eq $parent->owner_document->manakai_head) { |
| 2127 |
for my $el (@{$parent->child_nodes}) { |
| 2128 |
next unless $el->node_type == 1; # ELEMENT_NODE |
| 2129 |
unless ($el eq $item->{node}) { |
| 2130 |
## NOTE: Not the first child element. |
| 2131 |
$self->{onerror}->(node => $item->{node}, |
| 2132 |
type => 'element not allowed:meta charset', |
| 2133 |
level => $self->{level}->{must}); |
| 2134 |
} |
| 2135 |
last; |
| 2136 |
## NOTE: Entity references are not supported. |
| 2137 |
} |
| 2138 |
} else { |
| 2139 |
$self->{onerror}->(node => $item->{node}, |
| 2140 |
type => 'element not allowed:meta charset', |
| 2141 |
level => $self->{level}->{must}); |
| 2142 |
} |
| 2143 |
|
| 2144 |
unless ($item->{node}->owner_document->manakai_is_html) { |
| 2145 |
$self->{onerror}->(node => $item->{node}, |
| 2146 |
type => 'in XML:charset', |
| 2147 |
level => $self->{level}->{must}); |
| 2148 |
} |
| 2149 |
}; # $check_charset_decl |
| 2150 |
|
| 2151 |
my $check_charset = sub ($$) { |
| 2152 |
my ($attr, $charset_value) = @_; |
| 2153 |
|
| 2154 |
my $charset; |
| 2155 |
($charset, $charset_value) |
| 2156 |
= $HTMLCharsetChecker->($charset_value, $self, $attr); |
| 2157 |
|
| 2158 |
my $ic = $item->{node}->owner_document->input_encoding; |
| 2159 |
if (defined $ic) { |
| 2160 |
## TODO: Test for this case |
| 2161 |
my $ic_charset = $Message::Charset::Info::IANACharset->{$ic}; |
| 2162 |
if ($charset ne $ic_charset) { |
| 2163 |
$self->{onerror}->(node => $attr, |
| 2164 |
type => 'mismatched charset name', |
| 2165 |
text => $ic, |
| 2166 |
value => $charset_value, |
| 2167 |
level => $self->{level}->{must}); |
| 2168 |
} |
| 2169 |
} else { |
| 2170 |
## NOTE: MUST, but not checkable, since the document is not originally |
| 2171 |
## in serialized form (or the parser does not preserve the input |
| 2172 |
## encoding information). |
| 2173 |
$self->{onerror}->(node => $attr, |
| 2174 |
type => 'mismatched charset name not checked', |
| 2175 |
value => $charset_value, |
| 2176 |
level => $self->{level}->{uncertain}); |
| 2177 |
} |
| 2178 |
|
| 2179 |
if ($attr->get_user_data ('manakai_has_reference')) { |
| 2180 |
$self->{onerror}->(node => $attr, |
| 2181 |
type => 'charref in charset', |
| 2182 |
level => $self->{level}->{must}, |
| 2183 |
layer => 'syntax'); |
| 2184 |
} |
| 2185 |
}; # $check_charset |
| 2186 |
|
| 2187 |
## TODO: metadata conformance |
| 2188 |
|
| 2189 |
## TODO: pragma conformance |
| 2190 |
if (defined $http_equiv_attr) { ## An enumerated attribute |
| 2191 |
my $keyword = lc $http_equiv_attr->value; ## TODO: ascii case? |
| 2192 |
|
| 2193 |
if ($self->{has_http_equiv}->{$keyword}) { |
| 2194 |
$self->{onerror}->(type => 'duplicate http-equiv', value => $keyword, |
| 2195 |
node => $http_equiv_attr, |
| 2196 |
level => $self->{level}->{must}); |
| 2197 |
} else { |
| 2198 |
$self->{has_http_equiv}->{$keyword} = 1; |
| 2199 |
} |
| 2200 |
|
| 2201 |
if ($keyword eq 'content-type') { |
| 2202 |
## TODO: refs in "text/html; charset=" are not disallowed since rev.1275. |
| 2203 |
|
| 2204 |
$check_charset_decl->(); |
| 2205 |
if ($content_attr) { |
| 2206 |
my $content = $content_attr->value; |
| 2207 |
if ($content =~ m!^[Tt][Ee][Xx][Tt]/[Hh][Tt][Mm][Ll]; |
| 2208 |
[\x09\x0A\x0C\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt] |
| 2209 |
=(.+)\z!sx) { |
| 2210 |
$check_charset->($content_attr, $1); |
| 2211 |
} else { |
| 2212 |
$self->{onerror}->(node => $content_attr, |
| 2213 |
type => 'meta content-type syntax error', |
| 2214 |
level => $self->{level}->{must}); |
| 2215 |
} |
| 2216 |
} |
| 2217 |
} elsif ($keyword eq 'default-style') { |
| 2218 |
## ISSUE: Not defined yet in the spec. |
| 2219 |
} elsif ($keyword eq 'refresh') { |
| 2220 |
if ($content_attr) { |
| 2221 |
my $content = $content_attr->value; |
| 2222 |
if ($content =~ /\A[0-9]+\z/) { |
| 2223 |
## NOTE: Valid non-negative integer. |
| 2224 |
# |
| 2225 |
} elsif ($content =~ s/\A[0-9]+;[\x09\x0A\x0C\x0D\x20]+[Uu][Rr][Ll]=//) { |
| 2226 |
## ISSUE: Relative references are allowed? (RFC 3987 "IRI" is an absolute reference with optional fragment identifier.) |
| 2227 |
Whatpm::URIChecker->check_iri_reference ($content, sub { |
| 2228 |
$self->{onerror}->(value => $content, @_, node => $content_attr); |
| 2229 |
}, $self->{level}); |
| 2230 |
$self->{has_uri_attr} = 1; ## NOTE: One of "attributes with URIs". |
| 2231 |
|
| 2232 |
$element_state->{uri_info}->{content}->{node} = $content_attr; |
| 2233 |
$element_state->{uri_info}->{content}->{type}->{hyperlink} = 1; |
| 2234 |
## TODO: absolute |
| 2235 |
push @{$self->{return}->{uri}->{$content} ||= []}, |
| 2236 |
$element_state->{uri_info}->{content}; |
| 2237 |
} else { |
| 2238 |
$self->{onerror}->(node => $content_attr, |
| 2239 |
type => 'refresh:syntax error', |
| 2240 |
level => $self->{level}->{must}); |
| 2241 |
} |
| 2242 |
} |
| 2243 |
} else { |
| 2244 |
$self->{onerror}->(node => $http_equiv_attr, |
| 2245 |
type => 'enumerated:invalid', |
| 2246 |
level => $self->{level}->{must}); |
| 2247 |
} |
| 2248 |
} |
| 2249 |
|
| 2250 |
if (defined $charset_attr) { |
| 2251 |
$check_charset_decl->(); |
| 2252 |
$check_charset->($charset_attr, $charset_attr->value); |
| 2253 |
} |
| 2254 |
}, |
| 2255 |
}; |
| 2256 |
|
| 2257 |
$Element->{$HTML_NS}->{style} = { |
| 2258 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2259 |
%HTMLChecker, |
| 2260 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 2261 |
type => $HTMLIMTAttrChecker, ## TODO: MUST be a styling language |
| 2262 |
media => $HTMLMQAttrChecker, |
| 2263 |
scoped => $GetHTMLBooleanAttrChecker->('scoped'), |
| 2264 |
## NOTE: |title| has special semantics for |style|s, but is syntactically |
| 2265 |
## not different |
| 2266 |
}, { |
| 2267 |
%HTMLAttrStatus, |
| 2268 |
%XHTML2CommonAttrStatus, |
| 2269 |
dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2270 |
disabled => FEATURE_XHTML2_ED, |
| 2271 |
href => FEATURE_RDFA_PR | FEATURE_XHTML2_ED, |
| 2272 |
id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC, |
| 2273 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 2274 |
media => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2275 |
scoped => FEATURE_HTML5_DEFAULT, |
| 2276 |
title => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2277 |
type => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2278 |
}), |
| 2279 |
check_start => sub { |
| 2280 |
my ($self, $item, $element_state) = @_; |
| 2281 |
|
| 2282 |
## NOTE: |html:style| itself has no conformance creteria on content model. |
| 2283 |
my $type = $item->{node}->get_attribute_ns (undef, 'type'); |
| 2284 |
$type = 'text/css' unless defined $type; |
| 2285 |
if ($type =~ m[\A(?>(?>\x0D\x0A)?[\x09\x20])*([\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+)(?>(?>\x0D\x0A)?[\x09\x20])*/(?>(?>\x0D\x0A)?[\x09\x20])*([\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+)(?>(?>\x0D\x0A)?[\x09\x20])*\z]) { |
| 2286 |
$type = "$1/$2"; |
| 2287 |
$type =~ tr/A-Z/a-z/; ## NOTE: ASCII case-insensitive |
| 2288 |
} else { |
| 2289 |
## NOTE: We don't know how parameters are handled by UAs. According to |
| 2290 |
## HTML5 specification, <style> with unknown parameters in |type=""| |
| 2291 |
## must be ignored. |
| 2292 |
undef $type; |
| 2293 |
} |
| 2294 |
if (not defined $type) { |
| 2295 |
$element_state->{allow_element} = 1; # invalid type="" |
| 2296 |
} elsif ($type eq 'text/css') { |
| 2297 |
$element_state->{allow_element} = 0; |
| 2298 |
#} elsif ($type =~ m![/+][Xx][Mm][Ll]\z!) { |
| 2299 |
# ## NOTE: There is no definition for "XML-based styling language" in HTML5 |
| 2300 |
# $element_state->{allow_element} = 1; |
| 2301 |
} else { |
| 2302 |
$element_state->{allow_element} = 1; # unknown |
| 2303 |
} |
| 2304 |
$element_state->{style_type} = $type; |
| 2305 |
|
| 2306 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 2307 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 2308 |
|
| 2309 |
$element_state->{text} = ''; |
| 2310 |
}, |
| 2311 |
check_child_element => sub { |
| 2312 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 2313 |
$child_is_transparent, $element_state) = @_; |
| 2314 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 2315 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 2316 |
$self->{onerror}->(node => $child_el, |
| 2317 |
type => 'element not allowed:minus', |
| 2318 |
level => $self->{level}->{must}); |
| 2319 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 2320 |
# |
| 2321 |
} elsif ($element_state->{allow_element}) { |
| 2322 |
# |
| 2323 |
} else { |
| 2324 |
$self->{onerror}->(node => $child_el, type => 'element not allowed', |
| 2325 |
level => $self->{level}->{must}); |
| 2326 |
} |
| 2327 |
}, |
| 2328 |
check_child_text => sub { |
| 2329 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 2330 |
$element_state->{text} .= $child_node->data; |
| 2331 |
}, |
| 2332 |
check_end => sub { |
| 2333 |
my ($self, $item, $element_state) = @_; |
| 2334 |
if (not defined $element_state->{style_type}) { |
| 2335 |
## NOTE: Invalid type="" |
| 2336 |
# |
| 2337 |
} elsif ($element_state->{style_type} eq 'text/css') { |
| 2338 |
$self->{onsubdoc}->({s => $element_state->{text}, |
| 2339 |
container_node => $item->{node}, |
| 2340 |
media_type => 'text/css', is_char_string => 1}); |
| 2341 |
} elsif ($element_state->{style_type} =~ m![+/][Xx][Mm][Ll]\z!) { |
| 2342 |
## NOTE: XML content should be checked by THIS instance of checker |
| 2343 |
## as part of normal tree validation. However, we don't know of any |
| 2344 |
## XML-based styling language that can be used in HTML <style> element, |
| 2345 |
## such that we throw a "style language not supported" error. |
| 2346 |
$self->{onerror}->(node => $item->{node}, |
| 2347 |
type => 'XML style lang', |
| 2348 |
text => $element_state->{style_type}, |
| 2349 |
level => $self->{level}->{uncertain}); |
| 2350 |
} else { |
| 2351 |
## NOTE: Should we raise some kind of error for, |
| 2352 |
## say, <style type="text/plaion">? |
| 2353 |
$self->{onsubdoc}->({s => $element_state->{text}, |
| 2354 |
container_node => $item->{node}, |
| 2355 |
media_type => $element_state->{style_type}, |
| 2356 |
is_char_string => 1}); |
| 2357 |
} |
| 2358 |
|
| 2359 |
$HTMLChecker{check_end}->(@_); |
| 2360 |
}, |
| 2361 |
}; |
| 2362 |
## ISSUE: Relationship to significant content check? |
| 2363 |
|
| 2364 |
$Element->{$HTML_NS}->{body} = { |
| 2365 |
%HTMLFlowContentChecker, |
| 2366 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2367 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 2368 |
alink => $HTMLColorAttrChecker, |
| 2369 |
background => $HTMLURIAttrChecker, |
| 2370 |
bgcolor => $HTMLColorAttrChecker, |
| 2371 |
link => $HTMLColorAttrChecker, |
| 2372 |
text => $HTMLColorAttrChecker, |
| 2373 |
vlink => $HTMLColorAttrChecker, |
| 2374 |
}, { |
| 2375 |
%HTMLAttrStatus, |
| 2376 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 2377 |
alink => FEATURE_M12N10_REC_DEPRECATED, |
| 2378 |
background => FEATURE_M12N10_REC_DEPRECATED, |
| 2379 |
bgcolor => FEATURE_M12N10_REC_DEPRECATED, |
| 2380 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 2381 |
link => FEATURE_M12N10_REC_DEPRECATED, |
| 2382 |
onload => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 2383 |
onunload => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 2384 |
text => FEATURE_M12N10_REC_DEPRECATED, |
| 2385 |
vlink => FEATURE_M12N10_REC_DEPRECATED, |
| 2386 |
}), |
| 2387 |
check_start => sub { |
| 2388 |
my ($self, $item, $element_state) = @_; |
| 2389 |
|
| 2390 |
$element_state->{uri_info}->{background}->{type}->{embedded} = 1; |
| 2391 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 2392 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 2393 |
}, |
| 2394 |
}; |
| 2395 |
|
| 2396 |
$Element->{$HTML_NS}->{section} = { |
| 2397 |
%HTMLFlowContentChecker, |
| 2398 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED, |
| 2399 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 2400 |
}, { |
| 2401 |
%HTMLAttrStatus, |
| 2402 |
%XHTML2CommonAttrStatus, |
| 2403 |
}), |
| 2404 |
}; |
| 2405 |
|
| 2406 |
$Element->{$HTML_NS}->{nav} = { |
| 2407 |
status => FEATURE_HTML5_DEFAULT, |
| 2408 |
%HTMLFlowContentChecker, |
| 2409 |
}; |
| 2410 |
|
| 2411 |
$Element->{$HTML_NS}->{article} = { |
| 2412 |
status => FEATURE_HTML5_DEFAULT, |
| 2413 |
%HTMLFlowContentChecker, |
| 2414 |
}; |
| 2415 |
|
| 2416 |
$Element->{$HTML_NS}->{blockquote} = { |
| 2417 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2418 |
%HTMLFlowContentChecker, |
| 2419 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 2420 |
cite => $HTMLURIAttrChecker, |
| 2421 |
}, { |
| 2422 |
%HTMLAttrStatus, |
| 2423 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 2424 |
align => FEATURE_HTML2X_RFC, |
| 2425 |
cite => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2426 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 2427 |
sdaform => FEATURE_HTML20_RFC, |
| 2428 |
}), |
| 2429 |
check_start => sub { |
| 2430 |
my ($self, $item, $element_state) = @_; |
| 2431 |
|
| 2432 |
$element_state->{uri_info}->{cite}->{type}->{cite} = 1; |
| 2433 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 2434 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 2435 |
}, |
| 2436 |
}; |
| 2437 |
|
| 2438 |
$Element->{$HTML_NS}->{aside} = { |
| 2439 |
status => FEATURE_HTML5_DEFAULT, |
| 2440 |
%HTMLFlowContentChecker, |
| 2441 |
}; |
| 2442 |
|
| 2443 |
$Element->{$HTML_NS}->{h1} = { |
| 2444 |
%HTMLPhrasingContentChecker, |
| 2445 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2446 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 2447 |
align => $GetHTMLEnumeratedAttrChecker->({ |
| 2448 |
left => 1, center => 1, right => 1, justify => 1, |
| 2449 |
}), |
| 2450 |
}, { |
| 2451 |
%HTMLAttrStatus, |
| 2452 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 2453 |
align => FEATURE_M12N10_REC_DEPRECATED, |
| 2454 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 2455 |
sdaform => FEATURE_HTML20_RFC, |
| 2456 |
}), |
| 2457 |
check_start => sub { |
| 2458 |
my ($self, $item, $element_state) = @_; |
| 2459 |
$self->{flag}->{has_hn} = 1; |
| 2460 |
|
| 2461 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 2462 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 2463 |
}, |
| 2464 |
}; |
| 2465 |
|
| 2466 |
$Element->{$HTML_NS}->{h2} = {%{$Element->{$HTML_NS}->{h1}}}; |
| 2467 |
|
| 2468 |
$Element->{$HTML_NS}->{h3} = {%{$Element->{$HTML_NS}->{h1}}}; |
| 2469 |
|
| 2470 |
$Element->{$HTML_NS}->{h4} = {%{$Element->{$HTML_NS}->{h1}}}; |
| 2471 |
|
| 2472 |
$Element->{$HTML_NS}->{h5} = {%{$Element->{$HTML_NS}->{h1}}}; |
| 2473 |
|
| 2474 |
$Element->{$HTML_NS}->{h6} = {%{$Element->{$HTML_NS}->{h1}}}; |
| 2475 |
|
| 2476 |
## TODO: Explicit sectioning is "encouraged". |
| 2477 |
|
| 2478 |
$Element->{$HTML_NS}->{header} = { |
| 2479 |
status => FEATURE_HTML5_DEFAULT, |
| 2480 |
%HTMLFlowContentChecker, |
| 2481 |
check_start => sub { |
| 2482 |
my ($self, $item, $element_state) = @_; |
| 2483 |
$self->_add_minus_elements ($element_state, |
| 2484 |
{$HTML_NS => {qw/header 1 footer 1/}}, |
| 2485 |
$HTMLSectioningContent); |
| 2486 |
$element_state->{has_hn_original} = $self->{flag}->{has_hn}; |
| 2487 |
$self->{flag}->{has_hn} = 0; |
| 2488 |
|
| 2489 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 2490 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 2491 |
}, |
| 2492 |
check_end => sub { |
| 2493 |
my ($self, $item, $element_state) = @_; |
| 2494 |
$self->_remove_minus_elements ($element_state); |
| 2495 |
unless ($self->{flag}->{has_hn}) { |
| 2496 |
$self->{onerror}->(node => $item->{node}, |
| 2497 |
type => 'element missing:hn', |
| 2498 |
level => $self->{level}->{must}); |
| 2499 |
} |
| 2500 |
$self->{flag}->{has_hn} ||= $element_state->{has_hn_original}; |
| 2501 |
|
| 2502 |
$HTMLFlowContentChecker{check_end}->(@_); |
| 2503 |
}, |
| 2504 |
## ISSUE: <header><del><h1>...</h1></del></header> is conforming? |
| 2505 |
}; |
| 2506 |
|
| 2507 |
$Element->{$HTML_NS}->{footer} = { |
| 2508 |
status => FEATURE_HTML5_DEFAULT, |
| 2509 |
%HTMLFlowContentChecker, |
| 2510 |
check_start => sub { |
| 2511 |
my ($self, $item, $element_state) = @_; |
| 2512 |
$self->_add_minus_elements ($element_state, |
| 2513 |
{$HTML_NS => {footer => 1}}, |
| 2514 |
$HTMLSectioningContent, |
| 2515 |
$HTMLHeadingContent); |
| 2516 |
|
| 2517 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 2518 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 2519 |
}, |
| 2520 |
check_end => sub { |
| 2521 |
my ($self, $item, $element_state) = @_; |
| 2522 |
$self->_remove_minus_elements ($element_state); |
| 2523 |
|
| 2524 |
$HTMLFlowContentChecker{check_end}->(@_); |
| 2525 |
}, |
| 2526 |
}; |
| 2527 |
|
| 2528 |
$Element->{$HTML_NS}->{address} = { |
| 2529 |
%HTMLFlowContentChecker, |
| 2530 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2531 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 2532 |
## TODO: add test |
| 2533 |
#align => $GetHTMLEnumeratedAttrChecker->({ |
| 2534 |
# left => 1, center => 1, right => 1, justify => 1, |
| 2535 |
#}), |
| 2536 |
}, { |
| 2537 |
%HTMLAttrStatus, |
| 2538 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 2539 |
align => FEATURE_HTML2X_RFC, |
| 2540 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 2541 |
sdaform => FEATURE_HTML20_RFC, |
| 2542 |
sdapref => FEATURE_HTML20_RFC, |
| 2543 |
}), |
| 2544 |
check_start => sub { |
| 2545 |
my ($self, $item, $element_state) = @_; |
| 2546 |
$self->_add_minus_elements ($element_state, |
| 2547 |
{$HTML_NS => {footer => 1, address => 1}}, |
| 2548 |
$HTMLSectioningContent, $HTMLHeadingContent); |
| 2549 |
|
| 2550 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 2551 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 2552 |
}, |
| 2553 |
check_end => sub { |
| 2554 |
my ($self, $item, $element_state) = @_; |
| 2555 |
$self->_remove_minus_elements ($element_state); |
| 2556 |
|
| 2557 |
$HTMLFlowContentChecker{check_end}->(@_); |
| 2558 |
}, |
| 2559 |
}; |
| 2560 |
|
| 2561 |
$Element->{$HTML_NS}->{p} = { |
| 2562 |
%HTMLPhrasingContentChecker, |
| 2563 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2564 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 2565 |
align => $GetHTMLEnumeratedAttrChecker->({ |
| 2566 |
left => 1, center => 1, right => 1, justify => 1, |
| 2567 |
}), |
| 2568 |
}, { |
| 2569 |
%HTMLAttrStatus, |
| 2570 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 2571 |
align => FEATURE_M12N10_REC_DEPRECATED, |
| 2572 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 2573 |
sdaform => FEATURE_HTML20_RFC, |
| 2574 |
}), |
| 2575 |
}; |
| 2576 |
|
| 2577 |
$Element->{$HTML_NS}->{hr} = { |
| 2578 |
%HTMLEmptyChecker, |
| 2579 |
status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 2580 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 2581 |
## TODO: HTML4 |align|, |noshade|, |size|, |width| |
| 2582 |
}, { |
| 2583 |
%HTMLAttrStatus, |
| 2584 |
%HTMLM12NCommonAttrStatus, |
| 2585 |
align => FEATURE_M12N10_REC_DEPRECATED, |
| 2586 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 2587 |
noshade => FEATURE_M12N10_REC_DEPRECATED, |
| 2588 |
sdapref => FEATURE_HTML20_RFC, |
| 2589 |
size => FEATURE_M12N10_REC_DEPRECATED, |
| 2590 |
width => FEATURE_M12N10_REC_DEPRECATED, |
| 2591 |
}), |
| 2592 |
}; |
| 2593 |
|
| 2594 |
$Element->{$HTML_NS}->{br} = { |
| 2595 |
%HTMLEmptyChecker, |
| 2596 |
status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 2597 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 2598 |
clear => $GetHTMLEnumeratedAttrChecker->({ |
| 2599 |
left => 1, all => 1, right => 1, none => 1, |
| 2600 |
}), |
| 2601 |
}, { |
| 2602 |
%HTMLAttrStatus, |
| 2603 |
class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 2604 |
clear => FEATURE_M12N10_REC_DEPRECATED, |
| 2605 |
id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 2606 |
sdapref => FEATURE_HTML20_RFC, |
| 2607 |
style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 2608 |
title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 2609 |
}), |
| 2610 |
## NOTE: Blank line MUST NOT be used for presentation purpose. |
| 2611 |
## (This requirement is semantic so that we cannot check.) |
| 2612 |
}; |
| 2613 |
|
| 2614 |
$Element->{$HTML_NS}->{dialog} = { |
| 2615 |
status => FEATURE_HTML5_DEFAULT, |
| 2616 |
%HTMLChecker, |
| 2617 |
check_start => sub { |
| 2618 |
my ($self, $item, $element_state) = @_; |
| 2619 |
$element_state->{phase} = 'before dt'; |
| 2620 |
|
| 2621 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 2622 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 2623 |
}, |
| 2624 |
check_child_element => sub { |
| 2625 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 2626 |
$child_is_transparent, $element_state) = @_; |
| 2627 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 2628 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 2629 |
$self->{onerror}->(node => $child_el, |
| 2630 |
type => 'element not allowed:minus', |
| 2631 |
level => $self->{level}->{must}); |
| 2632 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 2633 |
# |
| 2634 |
} elsif ($element_state->{phase} eq 'before dt') { |
| 2635 |
if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') { |
| 2636 |
$element_state->{phase} = 'before dd'; |
| 2637 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') { |
| 2638 |
$self->{onerror} |
| 2639 |
->(node => $child_el, type => 'ps element missing', |
| 2640 |
text => 'dt', |
| 2641 |
level => $self->{level}->{must}); |
| 2642 |
$element_state->{phase} = 'before dt'; |
| 2643 |
} else { |
| 2644 |
$self->{onerror}->(node => $child_el, type => 'element not allowed', |
| 2645 |
level => $self->{level}->{must}); |
| 2646 |
} |
| 2647 |
} elsif ($element_state->{phase} eq 'before dd') { |
| 2648 |
if ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') { |
| 2649 |
$element_state->{phase} = 'before dt'; |
| 2650 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') { |
| 2651 |
$self->{onerror} |
| 2652 |
->(node => $child_el, type => 'ps element missing', |
| 2653 |
text => 'dd', |
| 2654 |
level => $self->{level}->{must}); |
| 2655 |
$element_state->{phase} = 'before dd'; |
| 2656 |
} else { |
| 2657 |
$self->{onerror}->(node => $child_el, type => 'element not allowed', |
| 2658 |
level => $self->{level}->{must}); |
| 2659 |
} |
| 2660 |
} else { |
| 2661 |
die "check_child_element: Bad |dialog| phase: $element_state->{phase}"; |
| 2662 |
} |
| 2663 |
}, |
| 2664 |
check_child_text => sub { |
| 2665 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 2666 |
if ($has_significant) { |
| 2667 |
$self->{onerror}->(node => $child_node, type => 'character not allowed', |
| 2668 |
level => $self->{level}->{must}); |
| 2669 |
} |
| 2670 |
}, |
| 2671 |
check_end => sub { |
| 2672 |
my ($self, $item, $element_state) = @_; |
| 2673 |
if ($element_state->{phase} eq 'before dd') { |
| 2674 |
$self->{onerror}->(node => $item->{node}, |
| 2675 |
type => 'child element missing', |
| 2676 |
text => 'dd', |
| 2677 |
level => $self->{level}->{must}); |
| 2678 |
} |
| 2679 |
|
| 2680 |
$HTMLChecker{check_end}->(@_); |
| 2681 |
}, |
| 2682 |
}; |
| 2683 |
|
| 2684 |
$Element->{$HTML_NS}->{pre} = { |
| 2685 |
%HTMLPhrasingContentChecker, |
| 2686 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2687 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 2688 |
width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), |
| 2689 |
}, { |
| 2690 |
%HTMLAttrStatus, |
| 2691 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 2692 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 2693 |
sdaform => FEATURE_HTML20_RFC, |
| 2694 |
width => FEATURE_M12N10_REC_DEPRECATED, |
| 2695 |
}), |
| 2696 |
check_end => sub { |
| 2697 |
my ($self, $item, $element_state) = @_; |
| 2698 |
|
| 2699 |
## TODO: Flag to enable/disable IDL checking? |
| 2700 |
my $class = $item->{node}->get_attribute_ns (undef, 'class'); |
| 2701 |
if ($class =~ /\bidl(?>-code)?\b/) { ## TODO: use classList.has |
| 2702 |
## NOTE: pre.idl: WHATWG, XHR, Selectors API, CSSOM specs |
| 2703 |
## NOTE: pre.code > code.idl-code: WebIDL spec |
| 2704 |
## NOTE: pre.idl-code: DOM1 spec |
| 2705 |
## NOTE: div.idl-code > pre: DOM, ProgressEvent specs |
| 2706 |
## NOTE: pre.schema: ReSpec-generated specs |
| 2707 |
$self->{onsubdoc}->({s => $item->{node}->text_content, |
| 2708 |
container_node => $item->{node}, |
| 2709 |
media_type => 'text/x-webidl', |
| 2710 |
is_char_string => 1}); |
| 2711 |
} |
| 2712 |
|
| 2713 |
$HTMLPhrasingContentChecker{check_end}->(@_); |
| 2714 |
}, |
| 2715 |
}; |
| 2716 |
|
| 2717 |
$Element->{$HTML_NS}->{ol} = { |
| 2718 |
%HTMLChecker, |
| 2719 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2720 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 2721 |
compact => $GetHTMLBooleanAttrChecker->('compact'), |
| 2722 |
reversed => $GetHTMLBooleanAttrChecker->('reversed'), |
| 2723 |
start => $HTMLIntegerAttrChecker, |
| 2724 |
## TODO: HTML4 |type| |
| 2725 |
}, { |
| 2726 |
%HTMLAttrStatus, |
| 2727 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 2728 |
align => FEATURE_HTML2X_RFC, |
| 2729 |
compact => FEATURE_M12N10_REC_DEPRECATED, |
| 2730 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 2731 |
reversed => FEATURE_HTML5_DEFAULT, |
| 2732 |
sdaform => FEATURE_HTML20_RFC, |
| 2733 |
#start => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED, |
| 2734 |
start => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 2735 |
type => FEATURE_M12N10_REC_DEPRECATED, |
| 2736 |
}), |
| 2737 |
check_child_element => sub { |
| 2738 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 2739 |
$child_is_transparent, $element_state) = @_; |
| 2740 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 2741 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 2742 |
$self->{onerror}->(node => $child_el, |
| 2743 |
type => 'element not allowed:minus', |
| 2744 |
level => $self->{level}->{must}); |
| 2745 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 2746 |
# |
| 2747 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'li') { |
| 2748 |
# |
| 2749 |
} else { |
| 2750 |
$self->{onerror}->(node => $child_el, type => 'element not allowed', |
| 2751 |
level => $self->{level}->{must}); |
| 2752 |
} |
| 2753 |
}, |
| 2754 |
check_child_text => sub { |
| 2755 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 2756 |
if ($has_significant) { |
| 2757 |
$self->{onerror}->(node => $child_node, type => 'character not allowed', |
| 2758 |
level => $self->{level}->{must}); |
| 2759 |
} |
| 2760 |
}, |
| 2761 |
}; |
| 2762 |
|
| 2763 |
$Element->{$HTML_NS}->{ul} = { |
| 2764 |
%{$Element->{$HTML_NS}->{ol}}, |
| 2765 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2766 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 2767 |
compact => $GetHTMLBooleanAttrChecker->('compact'), |
| 2768 |
## TODO: HTML4 |type| |
| 2769 |
## TODO: sdaform, align |
| 2770 |
}, { |
| 2771 |
%HTMLAttrStatus, |
| 2772 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 2773 |
align => FEATURE_HTML2X_RFC, |
| 2774 |
compact => FEATURE_M12N10_REC_DEPRECATED, |
| 2775 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 2776 |
sdaform => FEATURE_HTML20_RFC, |
| 2777 |
type => FEATURE_M12N10_REC_DEPRECATED, |
| 2778 |
}), |
| 2779 |
}; |
| 2780 |
|
| 2781 |
$Element->{$HTML_NS}->{dir} = { |
| 2782 |
## TODO: %block; is not allowed [HTML4] ## TODO: Empty list allowed? |
| 2783 |
%{$Element->{$HTML_NS}->{ul}}, |
| 2784 |
status => FEATURE_M12N10_REC_DEPRECATED, |
| 2785 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 2786 |
compact => $GetHTMLBooleanAttrChecker->('compact'), |
| 2787 |
}, { |
| 2788 |
%HTMLAttrStatus, |
| 2789 |
%HTMLM12NCommonAttrStatus, |
| 2790 |
align => FEATURE_HTML2X_RFC, |
| 2791 |
compact => FEATURE_M12N10_REC_DEPRECATED, |
| 2792 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 2793 |
sdaform => FEATURE_HTML20_RFC, |
| 2794 |
sdapref => FEATURE_HTML20_RFC, |
| 2795 |
}), |
| 2796 |
}; |
| 2797 |
|
| 2798 |
$Element->{$HTML_NS}->{li} = { |
| 2799 |
%HTMLFlowContentChecker, |
| 2800 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2801 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 2802 |
## TODO: HTML4 |type| |
| 2803 |
value => sub { |
| 2804 |
my ($self, $attr) = @_; |
| 2805 |
my $parent = $attr->owner_element->manakai_parent_element; |
| 2806 |
if (defined $parent) { |
| 2807 |
my $parent_ns = $parent->namespace_uri; |
| 2808 |
$parent_ns = '' unless defined $parent_ns; |
| 2809 |
my $parent_ln = $parent->manakai_local_name; |
| 2810 |
unless ($parent_ns eq $HTML_NS and $parent_ln eq 'ol') { |
| 2811 |
## ISSUE: ... |
| 2812 |
$self->{onerror}->(node => $attr, |
| 2813 |
type => 'unknown attribute', |
| 2814 |
level => $self->{level}->{uncertain}); |
| 2815 |
} |
| 2816 |
} |
| 2817 |
$HTMLIntegerAttrChecker->($self, $attr); |
| 2818 |
}, |
| 2819 |
}, { |
| 2820 |
%HTMLAttrStatus, |
| 2821 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 2822 |
align => FEATURE_HTML2X_RFC, |
| 2823 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 2824 |
sdaform => FEATURE_HTML20_RFC, |
| 2825 |
type => FEATURE_M12N10_REC_DEPRECATED, |
| 2826 |
#value => FEATURE_HTML5_DEFAULT | FEATURE_XHTMLBASIC11_CR | |
| 2827 |
# FEATURE_M12N10_REC_DEPRECATED, |
| 2828 |
value => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | |
| 2829 |
FEATURE_XHTMLBASIC11_CR | FEATURE_M12N10_REC, |
| 2830 |
}), |
| 2831 |
check_child_element => sub { |
| 2832 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 2833 |
$child_is_transparent, $element_state) = @_; |
| 2834 |
if ($self->{flag}->{in_menu}) { |
| 2835 |
$HTMLPhrasingContentChecker{check_child_element}->(@_); |
| 2836 |
} else { |
| 2837 |
$HTMLFlowContentChecker{check_child_element}->(@_); |
| 2838 |
} |
| 2839 |
}, |
| 2840 |
check_child_text => sub { |
| 2841 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 2842 |
if ($self->{flag}->{in_menu}) { |
| 2843 |
$HTMLPhrasingContentChecker{check_child_text}->(@_); |
| 2844 |
} else { |
| 2845 |
$HTMLFlowContentChecker{check_child_text}->(@_); |
| 2846 |
} |
| 2847 |
}, |
| 2848 |
}; |
| 2849 |
|
| 2850 |
$Element->{$HTML_NS}->{dl} = { |
| 2851 |
%HTMLChecker, |
| 2852 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2853 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 2854 |
compact => $GetHTMLBooleanAttrChecker->('compact'), |
| 2855 |
}, { |
| 2856 |
%HTMLAttrStatus, |
| 2857 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 2858 |
compact => FEATURE_M12N10_REC_DEPRECATED, |
| 2859 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 2860 |
sdaform => FEATURE_HTML20_RFC, |
| 2861 |
sdapref => FEATURE_HTML20_RFC, |
| 2862 |
type => FEATURE_M12N10_REC_DEPRECATED, |
| 2863 |
}), |
| 2864 |
check_start => sub { |
| 2865 |
my ($self, $item, $element_state) = @_; |
| 2866 |
$element_state->{phase} = 'before dt'; |
| 2867 |
|
| 2868 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 2869 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 2870 |
}, |
| 2871 |
check_child_element => sub { |
| 2872 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 2873 |
$child_is_transparent, $element_state) = @_; |
| 2874 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 2875 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 2876 |
$self->{onerror}->(node => $child_el, |
| 2877 |
type => 'element not allowed:minus', |
| 2878 |
level => $self->{level}->{must}); |
| 2879 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 2880 |
# |
| 2881 |
} elsif ($element_state->{phase} eq 'in dds') { |
| 2882 |
if ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') { |
| 2883 |
#$element_state->{phase} = 'in dds'; |
| 2884 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') { |
| 2885 |
$element_state->{phase} = 'in dts'; |
| 2886 |
} else { |
| 2887 |
$self->{onerror}->(node => $child_el, type => 'element not allowed', |
| 2888 |
level => $self->{level}->{must}); |
| 2889 |
} |
| 2890 |
} elsif ($element_state->{phase} eq 'in dts') { |
| 2891 |
if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') { |
| 2892 |
#$element_state->{phase} = 'in dts'; |
| 2893 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') { |
| 2894 |
$element_state->{phase} = 'in dds'; |
| 2895 |
} else { |
| 2896 |
$self->{onerror}->(node => $child_el, type => 'element not allowed', |
| 2897 |
level => $self->{level}->{must}); |
| 2898 |
} |
| 2899 |
} elsif ($element_state->{phase} eq 'before dt') { |
| 2900 |
if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') { |
| 2901 |
$element_state->{phase} = 'in dts'; |
| 2902 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') { |
| 2903 |
$self->{onerror} |
| 2904 |
->(node => $child_el, type => 'ps element missing', |
| 2905 |
text => 'dt', |
| 2906 |
level => $self->{level}->{must}); |
| 2907 |
$element_state->{phase} = 'in dds'; |
| 2908 |
} else { |
| 2909 |
$self->{onerror}->(node => $child_el, type => 'element not allowed', |
| 2910 |
level => $self->{level}->{must}); |
| 2911 |
} |
| 2912 |
} else { |
| 2913 |
die "check_child_element: Bad |dl| phase: $element_state->{phase}"; |
| 2914 |
} |
| 2915 |
}, |
| 2916 |
check_child_text => sub { |
| 2917 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 2918 |
if ($has_significant) { |
| 2919 |
$self->{onerror}->(node => $child_node, type => 'character not allowed', |
| 2920 |
level => $self->{level}->{must}); |
| 2921 |
} |
| 2922 |
}, |
| 2923 |
check_end => sub { |
| 2924 |
my ($self, $item, $element_state) = @_; |
| 2925 |
if ($element_state->{phase} eq 'in dts') { |
| 2926 |
$self->{onerror}->(node => $item->{node}, |
| 2927 |
type => 'child element missing', |
| 2928 |
text => 'dd', |
| 2929 |
level => $self->{level}->{must}); |
| 2930 |
} |
| 2931 |
|
| 2932 |
$HTMLChecker{check_end}->(@_); |
| 2933 |
}, |
| 2934 |
}; |
| 2935 |
|
| 2936 |
$Element->{$HTML_NS}->{dt} = { |
| 2937 |
%HTMLPhrasingContentChecker, |
| 2938 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2939 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 2940 |
%HTMLAttrStatus, |
| 2941 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 2942 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 2943 |
sdaform => FEATURE_HTML20_RFC, |
| 2944 |
}), |
| 2945 |
}; |
| 2946 |
|
| 2947 |
$Element->{$HTML_NS}->{dd} = { |
| 2948 |
%HTMLFlowContentChecker, |
| 2949 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2950 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 2951 |
%HTMLAttrStatus, |
| 2952 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 2953 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 2954 |
sdaform => FEATURE_HTML20_RFC, |
| 2955 |
}), |
| 2956 |
}; |
| 2957 |
|
| 2958 |
$Element->{$HTML_NS}->{a} = { |
| 2959 |
%HTMLTransparentChecker, |
| 2960 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2961 |
check_attrs => sub { |
| 2962 |
my ($self, $item, $element_state) = @_; |
| 2963 |
my %attr; |
| 2964 |
for my $attr (@{$item->{node}->attributes}) { |
| 2965 |
my $attr_ns = $attr->namespace_uri; |
| 2966 |
$attr_ns = '' unless defined $attr_ns; |
| 2967 |
my $attr_ln = $attr->manakai_local_name; |
| 2968 |
my $checker; |
| 2969 |
my $status; |
| 2970 |
if ($attr_ns eq '') { |
| 2971 |
$status = { |
| 2972 |
%HTMLAttrStatus, |
| 2973 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 2974 |
accesskey => FEATURE_M12N10_REC, |
| 2975 |
charset => FEATURE_M12N10_REC, |
| 2976 |
coords => FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2977 |
cryptopts => FEATURE_RFC2659, |
| 2978 |
dn => FEATURE_RFC2659, |
| 2979 |
href => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_CR | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2980 |
hreflang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2981 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 2982 |
media => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED, |
| 2983 |
methods => FEATURE_HTML20_RFC, |
| 2984 |
name => FEATURE_M12N10_REC_DEPRECATED, |
| 2985 |
nonce => FEATURE_RFC2659, |
| 2986 |
onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 2987 |
onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 2988 |
ping => FEATURE_HTML5_DEFAULT, |
| 2989 |
rel => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_CR | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2990 |
rev => FEATURE_RDFA_CR | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2991 |
sdapref => FEATURE_HTML20_RFC, |
| 2992 |
shape => FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2993 |
tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 2994 |
target => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 2995 |
type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 2996 |
urn => FEATURE_HTML20_RFC, |
| 2997 |
}->{$attr_ln}; |
| 2998 |
|
| 2999 |
$checker = { |
| 3000 |
accesskey => $HTMLAccesskeyAttrChecker, |
| 3001 |
charset => sub { |
| 3002 |
my ($self, $attr) = @_; |
| 3003 |
$HTMLCharsetChecker->($attr->value, @_); |
| 3004 |
}, |
| 3005 |
## TODO: HTML4 |coords| |
| 3006 |
target => $HTMLTargetAttrChecker, |
| 3007 |
href => $HTMLURIAttrChecker, |
| 3008 |
ping => $HTMLSpaceURIsAttrChecker, |
| 3009 |
rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) }, |
| 3010 |
rev => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(), |
| 3011 |
## TODO: HTML4 |shape| |
| 3012 |
media => $HTMLMQAttrChecker, |
| 3013 |
## TODO: HTML4/XHTML1 |name| |
| 3014 |
hreflang => $HTMLLanguageTagAttrChecker, |
| 3015 |
type => $HTMLIMTAttrChecker, |
| 3016 |
}->{$attr_ln}; |
| 3017 |
if ($checker) { |
| 3018 |
$attr{$attr_ln} = $attr; |
| 3019 |
} elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and |
| 3020 |
$attr_ln !~ /[A-Z]/) { |
| 3021 |
$checker = $HTMLDatasetAttrChecker; |
| 3022 |
$status = $HTMLDatasetAttrStatus; |
| 3023 |
} else { |
| 3024 |
$checker = $HTMLAttrChecker->{$attr_ln}; |
| 3025 |
} |
| 3026 |
} |
| 3027 |
$checker ||= $AttrChecker->{$attr_ns}->{$attr_ln} |
| 3028 |
|| $AttrChecker->{$attr_ns}->{''}; |
| 3029 |
$status ||= $AttrStatus->{$attr_ns}->{$attr_ln} |
| 3030 |
|| $AttrStatus->{$attr_ns}->{''}; |
| 3031 |
$status = FEATURE_ALLOWED if not defined $status and length $attr_ns; |
| 3032 |
|
| 3033 |
if ($checker) { |
| 3034 |
$checker->($self, $attr, $item, $element_state) if ref $checker; |
| 3035 |
} elsif ($attr_ns eq '' and not $status) { |
| 3036 |
# |
| 3037 |
} else { |
| 3038 |
$self->{onerror}->(node => $attr, |
| 3039 |
type => 'unknown attribute', |
| 3040 |
level => $self->{level}->{uncertain}); |
| 3041 |
## ISSUE: No conformance createria for unknown attributes in the spec |
| 3042 |
} |
| 3043 |
|
| 3044 |
$self->_attr_status_info ($attr, $status); |
| 3045 |
} |
| 3046 |
|
| 3047 |
$element_state->{in_a_href_original} = $self->{flag}->{in_a_href}; |
| 3048 |
if (defined $attr{href}) { |
| 3049 |
$self->{has_hyperlink_element} = 1; |
| 3050 |
$self->{flag}->{in_a_href} = 1; |
| 3051 |
} else { |
| 3052 |
for (qw/target ping rel media hreflang type/) { |
| 3053 |
if (defined $attr{$_}) { |
| 3054 |
$self->{onerror}->(node => $attr{$_}, |
| 3055 |
type => 'attribute not allowed', |
| 3056 |
level => $self->{level}->{must}); |
| 3057 |
} |
| 3058 |
} |
| 3059 |
} |
| 3060 |
|
| 3061 |
$element_state->{uri_info}->{href}->{type}->{hyperlink} = 1; |
| 3062 |
}, |
| 3063 |
check_start => sub { |
| 3064 |
my ($self, $item, $element_state) = @_; |
| 3065 |
$self->_add_minus_elements ($element_state, $HTMLInteractiveContent); |
| 3066 |
|
| 3067 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 3068 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 3069 |
}, |
| 3070 |
check_end => sub { |
| 3071 |
my ($self, $item, $element_state) = @_; |
| 3072 |
$self->_remove_minus_elements ($element_state); |
| 3073 |
delete $self->{flag}->{in_a_href} |
| 3074 |
unless $element_state->{in_a_href_original}; |
| 3075 |
|
| 3076 |
$HTMLTransparentChecker{check_end}->(@_); |
| 3077 |
}, |
| 3078 |
}; |
| 3079 |
|
| 3080 |
$Element->{$HTML_NS}->{q} = { |
| 3081 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 3082 |
%HTMLPhrasingContentChecker, |
| 3083 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 3084 |
cite => $HTMLURIAttrChecker, |
| 3085 |
}, { |
| 3086 |
%HTMLAttrStatus, |
| 3087 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 3088 |
cite => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 3089 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3090 |
sdapref => FEATURE_HTML2X_RFC, |
| 3091 |
sdasuff => FEATURE_HTML2X_RFC, |
| 3092 |
}), |
| 3093 |
check_start => sub { |
| 3094 |
my ($self, $item, $element_state) = @_; |
| 3095 |
|
| 3096 |
$element_state->{uri_info}->{cite}->{type}->{cite} = 1; |
| 3097 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 3098 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 3099 |
}, |
| 3100 |
}; |
| 3101 |
## TODO: "Quotation punctuation (such as quotation marks), if any, must be |
| 3102 |
## placed inside the <code>q</code> element." Though we cannot test the |
| 3103 |
## element against this requirement since it incluides a semantic bit, |
| 3104 |
## it might be possible to inform of the existence of quotation marks OUTSIDE |
| 3105 |
## the |q| element. |
| 3106 |
|
| 3107 |
$Element->{$HTML_NS}->{cite} = { |
| 3108 |
%HTMLPhrasingContentChecker, |
| 3109 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 3110 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 3111 |
%HTMLAttrStatus, |
| 3112 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 3113 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3114 |
sdaform => FEATURE_HTML20_RFC, |
| 3115 |
}), |
| 3116 |
}; |
| 3117 |
|
| 3118 |
$Element->{$HTML_NS}->{em} = { |
| 3119 |
%HTMLPhrasingContentChecker, |
| 3120 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 3121 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 3122 |
%HTMLAttrStatus, |
| 3123 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 3124 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3125 |
sdaform => FEATURE_HTML20_RFC, |
| 3126 |
}), |
| 3127 |
}; |
| 3128 |
|
| 3129 |
$Element->{$HTML_NS}->{strong} = { |
| 3130 |
%HTMLPhrasingContentChecker, |
| 3131 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 3132 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 3133 |
%HTMLAttrStatus, |
| 3134 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 3135 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3136 |
sdaform => FEATURE_HTML20_RFC, |
| 3137 |
}), |
| 3138 |
}; |
| 3139 |
|
| 3140 |
$Element->{$HTML_NS}->{small} = { |
| 3141 |
%HTMLPhrasingContentChecker, |
| 3142 |
status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 3143 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 3144 |
%HTMLAttrStatus, |
| 3145 |
%HTMLM12NCommonAttrStatus, |
| 3146 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3147 |
}), |
| 3148 |
}; |
| 3149 |
|
| 3150 |
$Element->{$HTML_NS}->{big} = { |
| 3151 |
%HTMLPhrasingContentChecker, |
| 3152 |
status => FEATURE_M12N10_REC, |
| 3153 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 3154 |
%HTMLAttrStatus, |
| 3155 |
%HTMLM12NCommonAttrStatus, |
| 3156 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3157 |
}), |
| 3158 |
}; |
| 3159 |
|
| 3160 |
$Element->{$HTML_NS}->{mark} = { |
| 3161 |
status => FEATURE_HTML5_DEFAULT, |
| 3162 |
%HTMLPhrasingContentChecker, |
| 3163 |
}; |
| 3164 |
|
| 3165 |
$Element->{$HTML_NS}->{dfn} = { |
| 3166 |
%HTMLPhrasingContentChecker, |
| 3167 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 3168 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 3169 |
%HTMLAttrStatus, |
| 3170 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 3171 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3172 |
}), |
| 3173 |
check_start => sub { |
| 3174 |
my ($self, $item, $element_state) = @_; |
| 3175 |
$self->_add_minus_elements ($element_state, {$HTML_NS => {dfn => 1}}); |
| 3176 |
|
| 3177 |
my $node = $item->{node}; |
| 3178 |
my $term = $node->get_attribute_ns (undef, 'title'); |
| 3179 |
unless (defined $term) { |
| 3180 |
for my $child (@{$node->child_nodes}) { |
| 3181 |
if ($child->node_type == 1) { # ELEMENT_NODE |
| 3182 |
if (defined $term) { |
| 3183 |
undef $term; |
| 3184 |
last; |
| 3185 |
} elsif ($child->manakai_local_name eq 'abbr') { |
| 3186 |
my $nsuri = $child->namespace_uri; |
| 3187 |
if (defined $nsuri and $nsuri eq $HTML_NS) { |
| 3188 |
my $attr = $child->get_attribute_node_ns (undef, 'title'); |
| 3189 |
if ($attr) { |
| 3190 |
$term = $attr->value; |
| 3191 |
} |
| 3192 |
} |
| 3193 |
} |
| 3194 |
} elsif ($child->node_type == 3 or $child->node_type == 4) { |
| 3195 |
## TEXT_NODE or CDATA_SECTION_NODE |
| 3196 |
if ($child->data =~ /\A[\x09\x0A\x0C\x0D\x20]+\z/) { # Inter-element whitespace |
| 3197 |
next; |
| 3198 |
} |
| 3199 |
undef $term; |
| 3200 |
last; |
| 3201 |
} |
| 3202 |
} |
| 3203 |
unless (defined $term) { |
| 3204 |
$term = $node->text_content; |
| 3205 |
} |
| 3206 |
} |
| 3207 |
if ($self->{term}->{$term}) { |
| 3208 |
push @{$self->{term}->{$term}}, $node; |
| 3209 |
} else { |
| 3210 |
$self->{term}->{$term} = [$node]; |
| 3211 |
} |
| 3212 |
## ISSUE: The HTML5 definition for the defined term does not work with |
| 3213 |
## |ruby| unless |dfn| has |title|. |
| 3214 |
|
| 3215 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 3216 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 3217 |
}, |
| 3218 |
check_end => sub { |
| 3219 |
my ($self, $item, $element_state) = @_; |
| 3220 |
$self->_remove_minus_elements ($element_state); |
| 3221 |
|
| 3222 |
$HTMLPhrasingContentChecker{check_end}->(@_); |
| 3223 |
}, |
| 3224 |
}; |
| 3225 |
|
| 3226 |
$Element->{$HTML_NS}->{abbr} = { |
| 3227 |
%HTMLPhrasingContentChecker, |
| 3228 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 3229 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 3230 |
%HTMLAttrStatus, |
| 3231 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 3232 |
full => FEATURE_XHTML2_ED, |
| 3233 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3234 |
}), |
| 3235 |
## NOTE: "If an abbreviation is pluralised, the expansion's grammatical |
| 3236 |
## number (plural vs singular) must match the grammatical number of the |
| 3237 |
## contents of the element." Though this can be checked by machine, |
| 3238 |
## it requires language-specific knowledge and dictionary, such that |
| 3239 |
## we don't support the check of the requirement. |
| 3240 |
## ISSUE: Is <abbr title="Cascading Style Sheets">CSS</abbr> conforming? |
| 3241 |
}; |
| 3242 |
|
| 3243 |
$Element->{$HTML_NS}->{acronym} = { |
| 3244 |
%HTMLPhrasingContentChecker, |
| 3245 |
status => FEATURE_M12N10_REC, |
| 3246 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 3247 |
%HTMLAttrStatus, |
| 3248 |
%HTMLM12NCommonAttrStatus, |
| 3249 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3250 |
}), |
| 3251 |
}; |
| 3252 |
|
| 3253 |
$Element->{$HTML_NS}->{time} = { |
| 3254 |
status => FEATURE_HTML5_DEFAULT, |
| 3255 |
%HTMLPhrasingContentChecker, |
| 3256 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 3257 |
datetime => sub { 1 }, # checked in |checker| |
| 3258 |
}, { |
| 3259 |
%HTMLAttrStatus, |
| 3260 |
%HTMLM12NCommonAttrStatus, |
| 3261 |
datetime => FEATURE_HTML5_FD, |
| 3262 |
}), |
| 3263 |
## TODO: Write tests |
| 3264 |
check_end => sub { |
| 3265 |
my ($self, $item, $element_state) = @_; |
| 3266 |
|
| 3267 |
my $attr = $item->{node}->get_attribute_node_ns (undef, 'datetime'); |
| 3268 |
my $input; |
| 3269 |
my $reg_sp; |
| 3270 |
my $input_node; |
| 3271 |
if ($attr) { |
| 3272 |
$input = $attr->value; |
| 3273 |
$reg_sp = qr/[\x09\x0A\x0C\x0D\x20]*/; |
| 3274 |
$input_node = $attr; |
| 3275 |
} else { |
| 3276 |
$input = $item->{node}->text_content; |
| 3277 |
$reg_sp = qr/\p{WhiteSpace}*/; |
| 3278 |
$input_node = $item->{node}; |
| 3279 |
|
| 3280 |
## ISSUE: What is the definition for "successfully extracts a date |
| 3281 |
## or time"? If the algorithm says the string is invalid but |
| 3282 |
## return some date or time, is it "successfully"? |
| 3283 |
} |
| 3284 |
|
| 3285 |
my $hour; |
| 3286 |
my $minute; |
| 3287 |
my $second; |
| 3288 |
if ($input =~ / |
| 3289 |
\A |
| 3290 |
$reg_sp |
| 3291 |
([0-9]+) # 1 |
| 3292 |
(?> |
| 3293 |
-([0-9]+) # 2 |
| 3294 |
-((?>[0-9]+)) # 3 # Use (?>) such that yyyy-mm-ddhh:mm does not match |
| 3295 |
$reg_sp |
| 3296 |
(?> |
| 3297 |
T |
| 3298 |
$reg_sp |
| 3299 |
)? |
| 3300 |
([0-9]+) # 4 |
| 3301 |
:([0-9]+) # 5 |
| 3302 |
(?> |
| 3303 |
:([0-9]+(?>\.[0-9]*)?|\.[0-9]*) # 6 |
| 3304 |
)? |
| 3305 |
$reg_sp |
| 3306 |
(?> |
| 3307 |
Z |
| 3308 |
$reg_sp |
| 3309 |
| |
| 3310 |
[+-]([0-9]+):([0-9]+) # 7, 8 |
| 3311 |
$reg_sp |
| 3312 |
)? |
| 3313 |
\z |
| 3314 |
| |
| 3315 |
:([0-9]+) # 9 |
| 3316 |
(?> |
| 3317 |
:([0-9]+(?>\.[0-9]*)?|\.[0-9]*) # 10 |
| 3318 |
)? |
| 3319 |
$reg_sp |
| 3320 |
\z |
| 3321 |
) |
| 3322 |
/x) { |
| 3323 |
if (defined $2) { ## YYYY-MM-DD T? hh:mm |
| 3324 |
if (length $1 != 4 or length $2 != 2 or length $3 != 2 or |
| 3325 |
length $4 != 2 or length $5 != 2) { |
| 3326 |
$self->{onerror}->(node => $input_node, |
| 3327 |
type => 'dateortime:syntax error', |
| 3328 |
level => $self->{level}->{must}); |
| 3329 |
} |
| 3330 |
|
| 3331 |
if (1 <= $2 and $2 <= 12) { |
| 3332 |
$self->{onerror}->(node => $input_node, type => 'datetime:bad day', |
| 3333 |
level => $self->{level}->{must}) |
| 3334 |
if $3 < 1 or |
| 3335 |
$3 > [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]->[$2]; |
| 3336 |
$self->{onerror}->(node => $input_node, type => 'datetime:bad day', |
| 3337 |
level => $self->{level}->{must}) |
| 3338 |
if $2 == 2 and $3 == 29 and |
| 3339 |
not ($1 % 400 == 0 or ($1 % 4 == 0 and $1 % 100 != 0)); |
| 3340 |
} else { |
| 3341 |
$self->{onerror}->(node => $input_node, |
| 3342 |
type => 'datetime:bad month', |
| 3343 |
level => $self->{level}->{must}); |
| 3344 |
} |
| 3345 |
|
| 3346 |
($hour, $minute, $second) = ($4, $5, $6); |
| 3347 |
|
| 3348 |
if (defined $7) { ## [+-]hh:mm |
| 3349 |
if (length $7 != 2 or length $8 != 2) { |
| 3350 |
$self->{onerror}->(node => $input_node, |
| 3351 |
type => 'dateortime:syntax error', |
| 3352 |
level => $self->{level}->{must}); |
| 3353 |
} |
| 3354 |
|
| 3355 |
$self->{onerror}->(node => $input_node, |
| 3356 |
type => 'datetime:bad timezone hour', |
| 3357 |
level => $self->{level}->{must}) |
| 3358 |
if $7 > 23; |
| 3359 |
$self->{onerror}->(node => $input_node, |
| 3360 |
type => 'datetime:bad timezone minute', |
| 3361 |
level => $self->{level}->{must}) |
| 3362 |
if $8 > 59; |
| 3363 |
} |
| 3364 |
} else { ## hh:mm |
| 3365 |
if (length $1 != 2 or length $9 != 2) { |
| 3366 |
$self->{onerror}->(node => $input_node, |
| 3367 |
type => qq'dateortime:syntax error', |
| 3368 |
level => $self->{level}->{must}); |
| 3369 |
} |
| 3370 |
|
| 3371 |
($hour, $minute, $second) = ($1, $9, $10); |
| 3372 |
} |
| 3373 |
|
| 3374 |
$self->{onerror}->(node => $input_node, type => 'datetime:bad hour', |
| 3375 |
level => $self->{level}->{must}) if $hour > 23; |
| 3376 |
$self->{onerror}->(node => $input_node, type => 'datetime:bad minute', |
| 3377 |
level => $self->{level}->{must}) if $minute > 59; |
| 3378 |
|
| 3379 |
if (defined $second) { ## s |
| 3380 |
## NOTE: Integer part of second don't have to have length of two. |
| 3381 |
|
| 3382 |
if (substr ($second, 0, 1) eq '.') { |
| 3383 |
$self->{onerror}->(node => $input_node, |
| 3384 |
type => 'dateortime:syntax error', |
| 3385 |
level => $self->{level}->{must}); |
| 3386 |
} |
| 3387 |
|
| 3388 |
$self->{onerror}->(node => $input_node, type => 'datetime:bad second', |
| 3389 |
level => $self->{level}->{must}) if $second >= 60; |
| 3390 |
} |
| 3391 |
} else { |
| 3392 |
$self->{onerror}->(node => $input_node, |
| 3393 |
type => 'dateortime:syntax error', |
| 3394 |
level => $self->{level}->{must}); |
| 3395 |
} |
| 3396 |
|
| 3397 |
$HTMLPhrasingContentChecker{check_end}->(@_); |
| 3398 |
}, |
| 3399 |
}; |
| 3400 |
|
| 3401 |
$Element->{$HTML_NS}->{meter} = { ## TODO: "The recommended way of giving the value is to include it as contents of the element" |
| 3402 |
## TODO: value inequalities (HTML5 revision 1463) |
| 3403 |
## TODO: content checking |
| 3404 |
## TODO: content or value must contain number (rev 2053) |
| 3405 |
status => FEATURE_HTML5_DEFAULT, |
| 3406 |
%HTMLPhrasingContentChecker, |
| 3407 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 3408 |
value => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }), |
| 3409 |
min => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }), |
| 3410 |
low => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }), |
| 3411 |
high => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }), |
| 3412 |
max => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }), |
| 3413 |
optimum => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }), |
| 3414 |
}, { |
| 3415 |
%HTMLAttrStatus, |
| 3416 |
high => FEATURE_HTML5_DEFAULT, |
| 3417 |
low => FEATURE_HTML5_DEFAULT, |
| 3418 |
max => FEATURE_HTML5_DEFAULT, |
| 3419 |
min => FEATURE_HTML5_DEFAULT, |
| 3420 |
optimum => FEATURE_HTML5_DEFAULT, |
| 3421 |
value => FEATURE_HTML5_DEFAULT, |
| 3422 |
}), |
| 3423 |
}; |
| 3424 |
|
| 3425 |
$Element->{$HTML_NS}->{progress} = { ## TODO: recommended to use content |
| 3426 |
status => FEATURE_HTML5_DEFAULT, |
| 3427 |
%HTMLPhrasingContentChecker, |
| 3428 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 3429 |
value => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift >= 0 }), |
| 3430 |
max => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift > 0 }), |
| 3431 |
}, { |
| 3432 |
%HTMLAttrStatus, |
| 3433 |
max => FEATURE_HTML5_DEFAULT, |
| 3434 |
value => FEATURE_HTML5_DEFAULT, |
| 3435 |
}), |
| 3436 |
}; |
| 3437 |
|
| 3438 |
$Element->{$HTML_NS}->{code} = { |
| 3439 |
%HTMLPhrasingContentChecker, |
| 3440 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 3441 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 3442 |
%HTMLAttrStatus, |
| 3443 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 3444 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3445 |
sdaform => FEATURE_HTML20_RFC, |
| 3446 |
}), |
| 3447 |
}; |
| 3448 |
|
| 3449 |
$Element->{$HTML_NS}->{var} = { |
| 3450 |
%HTMLPhrasingContentChecker, |
| 3451 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 3452 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 3453 |
%HTMLAttrStatus, |
| 3454 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 3455 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3456 |
sdaform => FEATURE_HTML20_RFC, |
| 3457 |
}), |
| 3458 |
}; |
| 3459 |
|
| 3460 |
$Element->{$HTML_NS}->{samp} = { |
| 3461 |
%HTMLPhrasingContentChecker, |
| 3462 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 3463 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 3464 |
%HTMLAttrStatus, |
| 3465 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 3466 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3467 |
sdaform => FEATURE_HTML20_RFC, |
| 3468 |
}), |
| 3469 |
}; |
| 3470 |
|
| 3471 |
$Element->{$HTML_NS}->{kbd} = { |
| 3472 |
%HTMLPhrasingContentChecker, |
| 3473 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 3474 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 3475 |
%HTMLAttrStatus, |
| 3476 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 3477 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3478 |
sdaform => FEATURE_HTML20_RFC, |
| 3479 |
}), |
| 3480 |
}; |
| 3481 |
|
| 3482 |
$Element->{$HTML_NS}->{sub} = { |
| 3483 |
%HTMLPhrasingContentChecker, |
| 3484 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 3485 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 3486 |
%HTMLAttrStatus, |
| 3487 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 3488 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3489 |
sdapref => FEATURE_HTML2X_RFC, |
| 3490 |
}), |
| 3491 |
}; |
| 3492 |
|
| 3493 |
$Element->{$HTML_NS}->{sup} = $Element->{$HTML_NS}->{sub}; |
| 3494 |
|
| 3495 |
$Element->{$HTML_NS}->{span} = { |
| 3496 |
%HTMLPhrasingContentChecker, |
| 3497 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 3498 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 3499 |
%HTMLAttrStatus, |
| 3500 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 3501 |
datafld => FEATURE_HTML4_REC_RESERVED, |
| 3502 |
dataformatas => FEATURE_HTML4_REC_RESERVED, |
| 3503 |
datasrc => FEATURE_HTML4_REC_RESERVED, |
| 3504 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3505 |
sdaform => FEATURE_HTML2X_RFC, |
| 3506 |
}), |
| 3507 |
}; |
| 3508 |
|
| 3509 |
$Element->{$HTML_NS}->{i} = { |
| 3510 |
%HTMLPhrasingContentChecker, |
| 3511 |
status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 3512 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 3513 |
%HTMLAttrStatus, |
| 3514 |
%HTMLM12NCommonAttrStatus, |
| 3515 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3516 |
sdaform => FEATURE_HTML20_RFC, |
| 3517 |
}), |
| 3518 |
}; |
| 3519 |
|
| 3520 |
$Element->{$HTML_NS}->{b} = $Element->{$HTML_NS}->{i}; |
| 3521 |
|
| 3522 |
$Element->{$HTML_NS}->{tt} = { |
| 3523 |
%HTMLPhrasingContentChecker, |
| 3524 |
status => FEATURE_M12N10_REC, |
| 3525 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 3526 |
%HTMLAttrStatus, |
| 3527 |
%HTMLM12NCommonAttrStatus, |
| 3528 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3529 |
sdaform => FEATURE_HTML20_RFC, |
| 3530 |
}), |
| 3531 |
}; |
| 3532 |
|
| 3533 |
$Element->{$HTML_NS}->{s} = { |
| 3534 |
%HTMLPhrasingContentChecker, |
| 3535 |
status => FEATURE_M12N10_REC_DEPRECATED, |
| 3536 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 3537 |
%HTMLAttrStatus, |
| 3538 |
%HTMLM12NCommonAttrStatus, |
| 3539 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3540 |
}), |
| 3541 |
}; |
| 3542 |
|
| 3543 |
$Element->{$HTML_NS}->{strike} = $Element->{$HTML_NS}->{s}; |
| 3544 |
|
| 3545 |
$Element->{$HTML_NS}->{u} = $Element->{$HTML_NS}->{s}; |
| 3546 |
|
| 3547 |
$Element->{$HTML_NS}->{bdo} = { |
| 3548 |
%HTMLPhrasingContentChecker, |
| 3549 |
status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 3550 |
check_attrs => sub { |
| 3551 |
my ($self, $item, $element_state) = @_; |
| 3552 |
$GetHTMLAttrsChecker->({}, { |
| 3553 |
%HTMLAttrStatus, |
| 3554 |
class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 3555 |
dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 3556 |
id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 3557 |
style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3558 |
title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 3559 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3560 |
sdapref => FEATURE_HTML2X_RFC, |
| 3561 |
sdasuff => FEATURE_HTML2X_RFC, |
| 3562 |
})->($self, $item, $element_state); |
| 3563 |
unless ($item->{node}->has_attribute_ns (undef, 'dir')) { |
| 3564 |
$self->{onerror}->(node => $item->{node}, |
| 3565 |
type => 'attribute missing', |
| 3566 |
text => 'dir', |
| 3567 |
level => $self->{level}->{must}); |
| 3568 |
} |
| 3569 |
}, |
| 3570 |
## ISSUE: The spec does not directly say that |dir| is a enumerated attr. |
| 3571 |
}; |
| 3572 |
|
| 3573 |
$Element->{$HTML_NS}->{ruby} = { |
| 3574 |
%HTMLPhrasingContentChecker, |
| 3575 |
status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC, |
| 3576 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 3577 |
%HTMLAttrStatus, |
| 3578 |
%HTMLM12NXHTML2CommonAttrStatus, # XHTML 1.1 & XHTML 2.0 & XHTML+RDFa 1.0 |
| 3579 |
lang => FEATURE_HTML5_DEFAULT, |
| 3580 |
}), |
| 3581 |
check_start => sub { |
| 3582 |
my ($self, $item, $element_state) = @_; |
| 3583 |
|
| 3584 |
$element_state->{phase} = 'before-rb'; |
| 3585 |
#$element_state->{has_sig} |
| 3586 |
|
| 3587 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 3588 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 3589 |
}, |
| 3590 |
## NOTE: (phrasing, (rt | (rp, rt, rp)))+ |
| 3591 |
check_child_element => sub { |
| 3592 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 3593 |
$child_is_transparent, $element_state) = @_; |
| 3594 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 3595 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 3596 |
$self->{onerror}->(node => $child_el, |
| 3597 |
type => 'element not allowed:minus', |
| 3598 |
level => $self->{level}->{must}); |
| 3599 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 3600 |
# |
| 3601 |
} elsif ($element_state->{phase} eq 'before-rb') { |
| 3602 |
if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) { |
| 3603 |
$element_state->{phase} = 'in-rb'; |
| 3604 |
} elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) { |
| 3605 |
$self->{onerror}->(node => $child_el, |
| 3606 |
level => $self->{level}->{should}, |
| 3607 |
type => 'no significant content before'); |
| 3608 |
$element_state->{phase} = 'after-rt'; |
| 3609 |
} elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) { |
| 3610 |
$self->{onerror}->(node => $child_el, |
| 3611 |
level => $self->{level}->{should}, |
| 3612 |
type => 'no significant content before'); |
| 3613 |
$element_state->{phase} = 'after-rp1'; |
| 3614 |
} else { |
| 3615 |
$self->{onerror}->(node => $child_el, |
| 3616 |
type => 'element not allowed:ruby base', |
| 3617 |
level => $self->{level}->{must}); |
| 3618 |
$element_state->{phase} = 'in-rb'; |
| 3619 |
} |
| 3620 |
} elsif ($element_state->{phase} eq 'in-rb') { |
| 3621 |
if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) { |
| 3622 |
#$element_state->{phase} = 'in-rb'; |
| 3623 |
} elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) { |
| 3624 |
unless ($element_state->{has_significant}) { |
| 3625 |
$self->{onerror}->(node => $child_el, |
| 3626 |
level => $self->{level}->{should}, |
| 3627 |
type => 'no significant content before'); |
| 3628 |
} |
| 3629 |
$element_state->{phase} = 'after-rt'; |
| 3630 |
} elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) { |
| 3631 |
unless ($element_state->{has_significant}) { |
| 3632 |
$self->{onerror}->(node => $child_el, |
| 3633 |
level => $self->{level}->{should}, |
| 3634 |
type => 'no significant content before'); |
| 3635 |
} |
| 3636 |
$element_state->{phase} = 'after-rp1'; |
| 3637 |
} else { |
| 3638 |
$self->{onerror}->(node => $child_el, |
| 3639 |
type => 'element not allowed:ruby base', |
| 3640 |
level => $self->{level}->{must}); |
| 3641 |
#$element_state->{phase} = 'in-rb'; |
| 3642 |
} |
| 3643 |
} elsif ($element_state->{phase} eq 'after-rt') { |
| 3644 |
if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) { |
| 3645 |
if ($element_state->{has_significant}) { |
| 3646 |
$element_state->{has_sig} = 1; |
| 3647 |
delete $element_state->{has_significant}; |
| 3648 |
} |
| 3649 |
$element_state->{phase} = 'in-rb'; |
| 3650 |
} elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) { |
| 3651 |
$self->{onerror}->(node => $child_el, |
| 3652 |
level => $self->{level}->{should}, |
| 3653 |
type => 'no significant content before'); |
| 3654 |
$element_state->{phase} = 'after-rp1'; |
| 3655 |
} elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) { |
| 3656 |
$self->{onerror}->(node => $child_el, |
| 3657 |
level => $self->{level}->{should}, |
| 3658 |
type => 'no significant content before'); |
| 3659 |
#$element_state->{phase} = 'after-rt'; |
| 3660 |
} else { |
| 3661 |
$self->{onerror}->(node => $child_el, |
| 3662 |
type => 'element not allowed:ruby base', |
| 3663 |
level => $self->{level}->{must}); |
| 3664 |
if ($element_state->{has_significant}) { |
| 3665 |
$element_state->{has_sig} = 1; |
| 3666 |
delete $element_state->{has_significant}; |
| 3667 |
} |
| 3668 |
$element_state->{phase} = 'in-rb'; |
| 3669 |
} |
| 3670 |
} elsif ($element_state->{phase} eq 'after-rp1') { |
| 3671 |
if ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) { |
| 3672 |
$element_state->{phase} = 'after-rp-rt'; |
| 3673 |
} elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) { |
| 3674 |
$self->{onerror}->(node => $child_el, |
| 3675 |
type => 'ps element missing', |
| 3676 |
text => 'rt', |
| 3677 |
level => $self->{level}->{must}); |
| 3678 |
$element_state->{phase} = 'after-rp2'; |
| 3679 |
} else { |
| 3680 |
$self->{onerror}->(node => $child_el, |
| 3681 |
type => 'ps element missing', |
| 3682 |
text => 'rt', |
| 3683 |
level => $self->{level}->{must}); |
| 3684 |
$self->{onerror}->(node => $child_el, |
| 3685 |
type => 'ps element missing', |
| 3686 |
text => 'rp', |
| 3687 |
level => $self->{level}->{must}); |
| 3688 |
unless ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) { |
| 3689 |
$self->{onerror}->(node => $child_el, |
| 3690 |
type => 'element not allowed:ruby base', |
| 3691 |
level => $self->{level}->{must}); |
| 3692 |
} |
| 3693 |
if ($element_state->{has_significant}) { |
| 3694 |
$element_state->{has_sig} = 1; |
| 3695 |
delete $element_state->{has_significant}; |
| 3696 |
} |
| 3697 |
$element_state->{phase} = 'in-rb'; |
| 3698 |
} |
| 3699 |
} elsif ($element_state->{phase} eq 'after-rp-rt') { |
| 3700 |
if ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) { |
| 3701 |
$element_state->{phase} = 'after-rp2'; |
| 3702 |
} elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) { |
| 3703 |
$self->{onerror}->(node => $child_el, |
| 3704 |
type => 'ps element missing', |
| 3705 |
text => 'rp', |
| 3706 |
level => $self->{level}->{must}); |
| 3707 |
$self->{onerror}->(node => $child_el, |
| 3708 |
level => $self->{level}->{should}, |
| 3709 |
type => 'no significant content before'); |
| 3710 |
$element_state->{phase} = 'after-rt'; |
| 3711 |
} else { |
| 3712 |
$self->{onerror}->(node => $child_el, |
| 3713 |
type => 'ps element missing', |
| 3714 |
text => 'rp', |
| 3715 |
level => $self->{level}->{must}); |
| 3716 |
unless ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) { |
| 3717 |
$self->{onerror}->(node => $child_el, |
| 3718 |
type => 'element not allowed:ruby base', |
| 3719 |
level => $self->{level}->{must}); |
| 3720 |
} |
| 3721 |
if ($element_state->{has_significant}) { |
| 3722 |
$element_state->{has_sig} = 1; |
| 3723 |
delete $element_state->{has_significant}; |
| 3724 |
} |
| 3725 |
$element_state->{phase} = 'in-rb'; |
| 3726 |
} |
| 3727 |
} elsif ($element_state->{phase} eq 'after-rp2') { |
| 3728 |
if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) { |
| 3729 |
if ($element_state->{has_significant}) { |
| 3730 |
$element_state->{has_sig} = 1; |
| 3731 |
delete $element_state->{has_significant}; |
| 3732 |
} |
| 3733 |
$element_state->{phase} = 'in-rb'; |
| 3734 |
} elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) { |
| 3735 |
$self->{onerror}->(node => $child_el, |
| 3736 |
level => $self->{level}->{should}, |
| 3737 |
type => 'no significant content before'); |
| 3738 |
$element_state->{phase} = 'after-rt'; |
| 3739 |
} elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) { |
| 3740 |
$self->{onerror}->(node => $child_el, |
| 3741 |
level => $self->{level}->{should}, |
| 3742 |
type => 'no significant content before'); |
| 3743 |
$element_state->{phase} = 'after-rp1'; |
| 3744 |
} else { |
| 3745 |
$self->{onerror}->(node => $child_el, |
| 3746 |
type => 'element not allowed:ruby base', |
| 3747 |
level => $self->{level}->{must}); |
| 3748 |
if ($element_state->{has_significant}) { |
| 3749 |
$element_state->{has_sig} = 1; |
| 3750 |
delete $element_state->{has_significant}; |
| 3751 |
} |
| 3752 |
$element_state->{phase} = 'in-rb'; |
| 3753 |
} |
| 3754 |
} else { |
| 3755 |
die "check_child_element: Bad |ruby| phase: $element_state->{phase}"; |
| 3756 |
} |
| 3757 |
}, |
| 3758 |
check_child_text => sub { |
| 3759 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 3760 |
if ($has_significant) { |
| 3761 |
if ($element_state->{phase} eq 'before-rb') { |
| 3762 |
$element_state->{phase} = 'in-rb'; |
| 3763 |
} elsif ($element_state->{phase} eq 'in-rb') { |
| 3764 |
# |
| 3765 |
} elsif ($element_state->{phase} eq 'after-rt' or |
| 3766 |
$element_state->{phase} eq 'after-rp2') { |
| 3767 |
$element_state->{phase} = 'in-rb'; |
| 3768 |
} elsif ($element_state->{phase} eq 'after-rp1') { |
| 3769 |
$self->{onerror}->(node => $child_node, |
| 3770 |
type => 'ps element missing', |
| 3771 |
text => 'rt', |
| 3772 |
level => $self->{level}->{must}); |
| 3773 |
$self->{onerror}->(node => $child_node, |
| 3774 |
type => 'ps element missing', |
| 3775 |
text => 'rp', |
| 3776 |
level => $self->{level}->{must}); |
| 3777 |
$element_state->{phase} = 'in-rb'; |
| 3778 |
} elsif ($element_state->{phase} eq 'after-rp-rt') { |
| 3779 |
$self->{onerror}->(node => $child_node, |
| 3780 |
type => 'ps element missing', |
| 3781 |
text => 'rp', |
| 3782 |
level => $self->{level}->{must}); |
| 3783 |
$element_state->{phase} = 'in-rb'; |
| 3784 |
} else { |
| 3785 |
die "check_child_text: Bad |ruby| phase: $element_state->{phase}"; |
| 3786 |
} |
| 3787 |
} |
| 3788 |
}, |
| 3789 |
check_end => sub { |
| 3790 |
my ($self, $item, $element_state) = @_; |
| 3791 |
$self->_remove_minus_elements ($element_state); |
| 3792 |
|
| 3793 |
if ($element_state->{phase} eq 'before-rb') { |
| 3794 |
$self->{onerror}->(node => $item->{node}, |
| 3795 |
level => $self->{level}->{should}, |
| 3796 |
type => 'no significant content'); |
| 3797 |
$self->{onerror}->(node => $item->{node}, |
| 3798 |
type => 'element missing', |
| 3799 |
text => 'rt', |
| 3800 |
level => $self->{level}->{must}); |
| 3801 |
} elsif ($element_state->{phase} eq 'in-rb') { |
| 3802 |
unless ($element_state->{has_significant}) { |
| 3803 |
$self->{onerror}->(node => $item->{node}, |
| 3804 |
level => $self->{level}->{should}, |
| 3805 |
type => 'no significant content at the end'); |
| 3806 |
} |
| 3807 |
$self->{onerror}->(node => $item->{node}, |
| 3808 |
type => 'element missing', |
| 3809 |
text => 'rt', |
| 3810 |
level => $self->{level}->{must}); |
| 3811 |
} elsif ($element_state->{phase} eq 'after-rt' or |
| 3812 |
$element_state->{phase} eq 'after-rp2') { |
| 3813 |
# |
| 3814 |
} elsif ($element_state->{phase} eq 'after-rp1') { |
| 3815 |
$self->{onerror}->(node => $item->{node}, |
| 3816 |
type => 'element missing', |
| 3817 |
text => 'rt', |
| 3818 |
level => $self->{level}->{must}); |
| 3819 |
$self->{onerror}->(node => $item->{node}, |
| 3820 |
type => 'element missing', |
| 3821 |
text => 'rp', |
| 3822 |
level => $self->{level}->{must}); |
| 3823 |
} elsif ($element_state->{phase} eq 'after-rp-rt') { |
| 3824 |
$self->{onerror}->(node => $item->{node}, |
| 3825 |
type => 'element missing', |
| 3826 |
text => 'rp', |
| 3827 |
level => $self->{level}->{must}); |
| 3828 |
} else { |
| 3829 |
die "check_child_text: Bad |ruby| phase: $element_state->{phase}"; |
| 3830 |
} |
| 3831 |
|
| 3832 |
## NOTE: A modified version of |check_end| of %AnyChecker. |
| 3833 |
if ($element_state->{has_significant} or $element_state->{has_sig}) { |
| 3834 |
$item->{real_parent_state}->{has_significant} = 1; |
| 3835 |
} |
| 3836 |
}, |
| 3837 |
}; |
| 3838 |
|
| 3839 |
$Element->{$HTML_NS}->{rt} = { |
| 3840 |
%HTMLPhrasingContentChecker, |
| 3841 |
status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC, |
| 3842 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 3843 |
%HTMLAttrStatus, |
| 3844 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 3845 |
lang => FEATURE_HTML5_DEFAULT, |
| 3846 |
}), |
| 3847 |
}; |
| 3848 |
|
| 3849 |
$Element->{$HTML_NS}->{rp} = { |
| 3850 |
%HTMLTextChecker, |
| 3851 |
status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC, |
| 3852 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 3853 |
%HTMLAttrStatus, |
| 3854 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 3855 |
lang => FEATURE_HTML5_DEFAULT, |
| 3856 |
}), |
| 3857 |
check_start => sub { |
| 3858 |
my ($self, $item, $element_state) = @_; |
| 3859 |
$element_state->{text} = ''; |
| 3860 |
|
| 3861 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 3862 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 3863 |
}, |
| 3864 |
check_child_text => sub { |
| 3865 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 3866 |
if ($has_significant) { |
| 3867 |
$element_state->{text} .= $child_node->data; |
| 3868 |
## NOTE: |<rp> <!---->(</rp>| is allowed. |
| 3869 |
} |
| 3870 |
}, |
| 3871 |
check_end => sub { |
| 3872 |
my ($self, $item, $element_state) = @_; |
| 3873 |
|
| 3874 |
my $p_class = ($item->{parent_state} and |
| 3875 |
$item->{parent_state}->{phase} and |
| 3876 |
$item->{parent_state}->{phase} eq 'after-rp2') |
| 3877 |
? qr/\p{Pe}/ : qr/\p{Ps}/; |
| 3878 |
if ($element_state->{text} =~ /\A$p_class\z/) { |
| 3879 |
#=~ /\A[\x09\x0A\x0C\x0D\x20]*${p_class}[\x09\x0A\x0C\x0D\x20]*\z/) { |
| 3880 |
# |
| 3881 |
} else { |
| 3882 |
$self->{onerror}->(node => $item->{node}, |
| 3883 |
type => 'rp:syntax error', |
| 3884 |
level => $self->{level}->{must}); |
| 3885 |
} |
| 3886 |
|
| 3887 |
$HTMLTextChecker{check_end}->(@_); |
| 3888 |
}, |
| 3889 |
}; |
| 3890 |
|
| 3891 |
=pod |
| 3892 |
|
| 3893 |
## TODO: |
| 3894 |
|
| 3895 |
+ |
| 3896 |
+ <p>Partly because of the confusion described above, authors are |
| 3897 |
+ strongly recommended to always mark up all paragraphs with the |
| 3898 |
+ <code>p</code> element, and to not have any <code>ins</code> or |
| 3899 |
+ <code>del</code> elements that cross across any <span |
| 3900 |
+ title="paragraph">implied paragraphs</span>.</p> |
| 3901 |
+ |
| 3902 |
(An informative note) |
| 3903 |
|
| 3904 |
<p><code>ins</code> elements should not cross <span |
| 3905 |
+ title="paragraph">implied paragraph</span> boundaries.</p> |
| 3906 |
(normative) |
| 3907 |
|
| 3908 |
+ <p><code>del</code> elements should not cross <span |
| 3909 |
+ title="paragraph">implied paragraph</span> boundaries.</p> |
| 3910 |
(normative) |
| 3911 |
|
| 3912 |
=cut |
| 3913 |
|
| 3914 |
$Element->{$HTML_NS}->{ins} = { |
| 3915 |
%HTMLTransparentChecker, |
| 3916 |
status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 3917 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 3918 |
cite => $HTMLURIAttrChecker, |
| 3919 |
datetime => $HTMLDatetimeAttrChecker, |
| 3920 |
}, { |
| 3921 |
%HTMLAttrStatus, |
| 3922 |
%HTMLM12NCommonAttrStatus, |
| 3923 |
cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 3924 |
datetime => FEATURE_HTML5_FD | FEATURE_M12N10_REC, |
| 3925 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3926 |
}), |
| 3927 |
check_start => sub { |
| 3928 |
my ($self, $item, $element_state) = @_; |
| 3929 |
|
| 3930 |
$element_state->{uri_info}->{cite}->{type}->{cite} = 1; |
| 3931 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 3932 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 3933 |
}, |
| 3934 |
}; |
| 3935 |
|
| 3936 |
$Element->{$HTML_NS}->{del} = { |
| 3937 |
%HTMLTransparentChecker, |
| 3938 |
status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 3939 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 3940 |
cite => $HTMLURIAttrChecker, |
| 3941 |
datetime => $HTMLDatetimeAttrChecker, |
| 3942 |
}, { |
| 3943 |
%HTMLAttrStatus, |
| 3944 |
%HTMLM12NCommonAttrStatus, |
| 3945 |
cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 3946 |
datetime => FEATURE_HTML5_FD | FEATURE_M12N10_REC, |
| 3947 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 3948 |
}), |
| 3949 |
check_end => sub { |
| 3950 |
my ($self, $item, $element_state) = @_; |
| 3951 |
if ($element_state->{has_significant}) { |
| 3952 |
## NOTE: Significantness flag does not propagate. |
| 3953 |
} elsif ($item->{transparent}) { |
| 3954 |
# |
| 3955 |
} else { |
| 3956 |
$self->{onerror}->(node => $item->{node}, |
| 3957 |
level => $self->{level}->{should}, |
| 3958 |
type => 'no significant content'); |
| 3959 |
} |
| 3960 |
}, |
| 3961 |
check_start => sub { |
| 3962 |
my ($self, $item, $element_state) = @_; |
| 3963 |
|
| 3964 |
$element_state->{uri_info}->{cite}->{type}->{cite} = 1; |
| 3965 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 3966 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 3967 |
}, |
| 3968 |
}; |
| 3969 |
|
| 3970 |
$Element->{$HTML_NS}->{figure} = { |
| 3971 |
%HTMLFlowContentChecker, |
| 3972 |
status => FEATURE_HTML5_FD, |
| 3973 |
## NOTE: legend, Flow | Flow, legend? |
| 3974 |
check_child_element => sub { |
| 3975 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 3976 |
$child_is_transparent, $element_state) = @_; |
| 3977 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 3978 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 3979 |
$self->{onerror}->(node => $child_el, |
| 3980 |
type => 'element not allowed:minus', |
| 3981 |
level => $self->{level}->{must}); |
| 3982 |
$element_state->{has_non_legend} = 1; |
| 3983 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 3984 |
# |
| 3985 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') { |
| 3986 |
if ($element_state->{has_legend_at_first}) { |
| 3987 |
$self->{onerror}->(node => $child_el, |
| 3988 |
type => 'element not allowed:figure legend', |
| 3989 |
level => $self->{level}->{must}); |
| 3990 |
} elsif ($element_state->{has_legend}) { |
| 3991 |
$self->{onerror}->(node => $element_state->{has_legend}, |
| 3992 |
type => 'element not allowed:figure legend', |
| 3993 |
level => $self->{level}->{must}); |
| 3994 |
$element_state->{has_legend} = $child_el; |
| 3995 |
} elsif ($element_state->{has_non_legend}) { |
| 3996 |
$element_state->{has_legend} = $child_el; |
| 3997 |
} else { |
| 3998 |
$element_state->{has_legend_at_first} = 1; |
| 3999 |
} |
| 4000 |
delete $element_state->{has_non_legend}; |
| 4001 |
} else { |
| 4002 |
$HTMLFlowContentChecker{check_child_element}->(@_); |
| 4003 |
$element_state->{has_non_legend} = 1 unless $child_is_transparent; |
| 4004 |
} |
| 4005 |
}, |
| 4006 |
check_child_text => sub { |
| 4007 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 4008 |
if ($has_significant) { |
| 4009 |
$element_state->{has_non_legend} = 1; |
| 4010 |
} |
| 4011 |
}, |
| 4012 |
check_end => sub { |
| 4013 |
my ($self, $item, $element_state) = @_; |
| 4014 |
|
| 4015 |
if ($element_state->{has_legend_at_first}) { |
| 4016 |
# |
| 4017 |
} elsif ($element_state->{has_legend}) { |
| 4018 |
if ($element_state->{has_non_legend}) { |
| 4019 |
$self->{onerror}->(node => $element_state->{has_legend}, |
| 4020 |
type => 'element not allowed:figure legend', |
| 4021 |
level => $self->{level}->{must}); |
| 4022 |
} |
| 4023 |
} |
| 4024 |
|
| 4025 |
$HTMLFlowContentChecker{check_end}->(@_); |
| 4026 |
## ISSUE: |<figure><legend>aa</legend></figure>| should be an error? |
| 4027 |
}, |
| 4028 |
}; |
| 4029 |
## TODO: Test for <nest/> in <figure/> |
| 4030 |
|
| 4031 |
my $AttrCheckerNotImplemented = sub { |
| 4032 |
my ($self, $attr) = @_; |
| 4033 |
$self->{onerror}->(node => $attr, |
| 4034 |
type => 'unknown attribute', |
| 4035 |
level => $self->{level}->{uncertain}); |
| 4036 |
}; |
| 4037 |
|
| 4038 |
$Element->{$HTML_NS}->{img} = { |
| 4039 |
%HTMLEmptyChecker, |
| 4040 |
status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 4041 |
check_attrs => sub { |
| 4042 |
my ($self, $item, $element_state) = @_; |
| 4043 |
$GetHTMLAttrsChecker->({ |
| 4044 |
align => $GetHTMLEnumeratedAttrChecker->({ |
| 4045 |
bottom => 1, middle => 1, top => 1, left => 1, right => 1, |
| 4046 |
}), |
| 4047 |
alt => sub { }, ## NOTE: No syntactical requirement |
| 4048 |
border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), |
| 4049 |
src => $HTMLURIAttrChecker, |
| 4050 |
usemap => $HTMLUsemapAttrChecker, |
| 4051 |
hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), |
| 4052 |
ismap => sub { |
| 4053 |
my ($self, $attr, $parent_item) = @_; |
| 4054 |
if (not $self->{flag}->{in_a_href}) { |
| 4055 |
$self->{onerror}->(node => $attr, |
| 4056 |
type => 'attribute not allowed:ismap', |
| 4057 |
level => $self->{level}->{must}); |
| 4058 |
} |
| 4059 |
$GetHTMLBooleanAttrChecker->('ismap')->($self, $attr, $parent_item); |
| 4060 |
}, |
| 4061 |
longdesc => $HTMLURIAttrChecker, |
| 4062 |
## TODO: HTML4 |name| |
| 4063 |
height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet |
| 4064 |
vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), |
| 4065 |
width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet |
| 4066 |
}, { |
| 4067 |
%HTMLAttrStatus, |
| 4068 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 4069 |
align => FEATURE_M12N10_REC_DEPRECATED, |
| 4070 |
alt => FEATURE_HTML5_WD | FEATURE_M12N10_REC, |
| 4071 |
border => FEATURE_M12N10_REC_DEPRECATED, |
| 4072 |
height => FEATURE_HTML5_WD | FEATURE_M12N10_REC, |
| 4073 |
hspace => FEATURE_M12N10_REC_DEPRECATED, |
| 4074 |
ismap => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 4075 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 4076 |
longdesc => FEATURE_M12N10_REC, |
| 4077 |
name => FEATURE_M12N10_REC_DEPRECATED, |
| 4078 |
sdapref => FEATURE_HTML20_RFC, |
| 4079 |
src => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 4080 |
usemap => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 4081 |
vspace => FEATURE_M12N10_REC_DEPRECATED, |
| 4082 |
width => FEATURE_HTML5_WD | FEATURE_M12N10_REC, |
| 4083 |
})->($self, $item, $element_state); |
| 4084 |
unless ($item->{node}->has_attribute_ns (undef, 'alt')) { |
| 4085 |
$self->{onerror}->(node => $item->{node}, |
| 4086 |
type => 'attribute missing', |
| 4087 |
text => 'alt', |
| 4088 |
level => $self->{level}->{should}); |
| 4089 |
## TODO: ... |
| 4090 |
} |
| 4091 |
unless ($item->{node}->has_attribute_ns (undef, 'src')) { |
| 4092 |
$self->{onerror}->(node => $item->{node}, |
| 4093 |
type => 'attribute missing', |
| 4094 |
text => 'src', |
| 4095 |
level => $self->{level}->{must}); |
| 4096 |
} |
| 4097 |
|
| 4098 |
## TODO: external resource check |
| 4099 |
|
| 4100 |
$element_state->{uri_info}->{src}->{type}->{embedded} = 1; |
| 4101 |
$element_state->{uri_info}->{lowsrc}->{type}->{embedded} = 1; |
| 4102 |
$element_state->{uri_info}->{dynsrc}->{type}->{embedded} = 1; |
| 4103 |
$element_state->{uri_info}->{longdesc}->{type}->{cite} = 1; |
| 4104 |
}, |
| 4105 |
}; |
| 4106 |
|
| 4107 |
$Element->{$HTML_NS}->{iframe} = { |
| 4108 |
%HTMLTextChecker, |
| 4109 |
status => FEATURE_HTML5_WD | FEATURE_M12N10_REC, |
| 4110 |
## NOTE: Not part of M12N10 Strict |
| 4111 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 4112 |
height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet |
| 4113 |
name => $HTMLBrowsingContextNameAttrChecker, |
| 4114 |
sandbox => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->({ |
| 4115 |
'allow-same-origin' => 1, 'allow-forms' => 1, 'allow-scripts' => 1, |
| 4116 |
}), |
| 4117 |
seemless => $GetHTMLBooleanAttrChecker->('seemless'), |
| 4118 |
src => $HTMLURIAttrChecker, |
| 4119 |
width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet |
| 4120 |
}, { |
| 4121 |
%HTMLAttrStatus, |
| 4122 |
%HTMLM12NCommonAttrStatus, |
| 4123 |
align => FEATURE_XHTML10_REC, |
| 4124 |
class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4125 |
frameborder => FEATURE_M12N10_REC, |
| 4126 |
height => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4127 |
id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4128 |
longdesc => FEATURE_M12N10_REC, |
| 4129 |
marginheight => FEATURE_M12N10_REC, |
| 4130 |
marginwidth => FEATURE_M12N10_REC, |
| 4131 |
#name => FEATURE_HTML5_WD | FEATURE_M12N10_REC_DEPRECATED, |
| 4132 |
name => FEATURE_HTML5_WD | FEATURE_M12N10_REC, |
| 4133 |
sandbox => FEATURE_HTML5_WD, |
| 4134 |
scrolling => FEATURE_M12N10_REC, |
| 4135 |
seemless => FEATURE_HTML5_WD, |
| 4136 |
src => FEATURE_HTML5_WD | FEATURE_M12N10_REC, |
| 4137 |
title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4138 |
width => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4139 |
}), |
| 4140 |
check_start => sub { |
| 4141 |
my ($self, $item, $element_state) = @_; |
| 4142 |
|
| 4143 |
$element_state->{uri_info}->{src}->{type}->{embedded} = 1; |
| 4144 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 4145 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 4146 |
}, |
| 4147 |
}; |
| 4148 |
|
| 4149 |
$Element->{$HTML_NS}->{embed} = { |
| 4150 |
%HTMLEmptyChecker, |
| 4151 |
status => FEATURE_HTML5_WD, |
| 4152 |
check_attrs => sub { |
| 4153 |
my ($self, $item, $element_state) = @_; |
| 4154 |
my $has_src; |
| 4155 |
for my $attr (@{$item->{node}->attributes}) { |
| 4156 |
my $attr_ns = $attr->namespace_uri; |
| 4157 |
$attr_ns = '' unless defined $attr_ns; |
| 4158 |
my $attr_ln = $attr->manakai_local_name; |
| 4159 |
my $checker; |
| 4160 |
|
| 4161 |
my $status = { |
| 4162 |
%HTMLAttrStatus, |
| 4163 |
height => FEATURE_HTML5_DEFAULT, |
| 4164 |
src => FEATURE_HTML5_WD, |
| 4165 |
type => FEATURE_HTML5_WD, |
| 4166 |
width => FEATURE_HTML5_DEFAULT, |
| 4167 |
}->{$attr_ln}; |
| 4168 |
|
| 4169 |
if ($attr_ns eq '') { |
| 4170 |
if ($attr_ln eq 'src') { |
| 4171 |
$checker = $HTMLURIAttrChecker; |
| 4172 |
$has_src = 1; |
| 4173 |
} elsif ($attr_ln eq 'type') { |
| 4174 |
$checker = $HTMLIMTAttrChecker; |
| 4175 |
} elsif ($attr_ln eq 'width' or $attr_ln eq 'height') { |
| 4176 |
$checker = $AttrCheckerNotImplemented; ## TODO: because spec does not define them yet. |
| 4177 |
} elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and |
| 4178 |
$attr_ln !~ /[A-Z]/) { |
| 4179 |
$checker = $HTMLDatasetAttrChecker; |
| 4180 |
$status = $HTMLDatasetAttrStatus; |
| 4181 |
} elsif ($attr_ln !~ /^[Xx][Mm][Ll]/ and |
| 4182 |
$attr_ln !~ /[A-Z]/ and |
| 4183 |
$attr_ln =~ /\A\p{InXML_NCNameStartChar10}\p{InXMLNCNameChar10}*\z/) { |
| 4184 |
$checker = $HTMLAttrChecker->{$attr_ln} |
| 4185 |
|| sub { }; ## NOTE: Any local attribute is ok. |
| 4186 |
$status = FEATURE_HTML5_WD | FEATURE_ALLOWED; |
| 4187 |
} else { |
| 4188 |
$checker = $HTMLAttrChecker->{$attr_ln}; |
| 4189 |
} |
| 4190 |
} |
| 4191 |
$checker ||= $AttrChecker->{$attr_ns}->{$attr_ln} |
| 4192 |
|| $AttrChecker->{$attr_ns}->{''}; |
| 4193 |
$status ||= $AttrStatus->{$attr_ns}->{$attr_ln} |
| 4194 |
|| $AttrStatus->{$attr_ns}->{''}; |
| 4195 |
$status = FEATURE_ALLOWED if not defined $status and length $attr_ns; |
| 4196 |
|
| 4197 |
if ($checker) { |
| 4198 |
$checker->($self, $attr, $item, $element_state); |
| 4199 |
} elsif ($attr_ns eq '' and not $status) { |
| 4200 |
# |
| 4201 |
} else { |
| 4202 |
$self->{onerror}->(node => $attr, |
| 4203 |
type => 'unknown attribute', |
| 4204 |
level => $self->{level}->{uncertain}); |
| 4205 |
## ISSUE: No conformance createria for global attributes in the spec |
| 4206 |
} |
| 4207 |
|
| 4208 |
$self->_attr_status_info ($attr, $status); |
| 4209 |
} |
| 4210 |
|
| 4211 |
unless ($has_src) { |
| 4212 |
$self->{onerror}->(node => $item->{node}, |
| 4213 |
type => 'attribute missing', |
| 4214 |
text => 'src', |
| 4215 |
level => $self->{level}->{info}); |
| 4216 |
## NOTE: <embed> without src="" is allowed since revision 1929. |
| 4217 |
## We issues an informational message since <embed> w/o src="" |
| 4218 |
## is likely an authoring error. |
| 4219 |
} |
| 4220 |
|
| 4221 |
## TODO: external resource check |
| 4222 |
|
| 4223 |
$element_state->{uri_info}->{src}->{type}->{embedded} = 1; |
| 4224 |
}, |
| 4225 |
}; |
| 4226 |
|
| 4227 |
## TODO: |
| 4228 |
## {applet} FEATURE_M12N10_REC_DEPRECATED |
| 4229 |
## class, id, title, alt, archive, code, codebase, height, object, width name style,hspace,vspace(xhtml10) |
| 4230 |
|
| 4231 |
$Element->{$HTML_NS}->{object} = { |
| 4232 |
%HTMLTransparentChecker, |
| 4233 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 4234 |
check_attrs => sub { |
| 4235 |
my ($self, $item, $element_state) = @_; |
| 4236 |
$GetHTMLAttrsChecker->({ |
| 4237 |
align => $GetHTMLEnumeratedAttrChecker->({ |
| 4238 |
bottom => 1, middle => 1, top => 1, left => 1, right => 1, |
| 4239 |
}), |
| 4240 |
archive => $HTMLSpaceURIsAttrChecker, |
| 4241 |
## TODO: Relative to @codebase |
| 4242 |
border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), |
| 4243 |
classid => $HTMLURIAttrChecker, |
| 4244 |
codebase => $HTMLURIAttrChecker, |
| 4245 |
codetype => $HTMLIMTAttrChecker, |
| 4246 |
## TODO: "RECOMMENDED when |classid| is specified" [HTML4] |
| 4247 |
data => $HTMLURIAttrChecker, |
| 4248 |
declare => $GetHTMLBooleanAttrChecker->('declare'), |
| 4249 |
## NOTE: "The object MUST be instantiated by a subsequent OBJECT ..." |
| 4250 |
## [HTML4] but we don't know how to test this. |
| 4251 |
hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), |
| 4252 |
name => $HTMLBrowsingContextNameAttrChecker, |
| 4253 |
standby => sub {}, ## NOTE: %Text; in HTML4 |
| 4254 |
type => $HTMLIMTAttrChecker, |
| 4255 |
usemap => $HTMLUsemapAttrChecker, |
| 4256 |
height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet |
| 4257 |
vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), |
| 4258 |
width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet |
| 4259 |
}, { |
| 4260 |
%HTMLAttrStatus, |
| 4261 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 4262 |
align => FEATURE_XHTML10_REC, |
| 4263 |
archive => FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 4264 |
border => FEATURE_XHTML10_REC, |
| 4265 |
classid => FEATURE_M12N10_REC, |
| 4266 |
codebase => FEATURE_M12N10_REC, |
| 4267 |
codetype => FEATURE_M12N10_REC, |
| 4268 |
'content-length' => FEATURE_XHTML2_ED, |
| 4269 |
data => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4270 |
datafld => FEATURE_HTML4_REC_RESERVED, |
| 4271 |
dataformatas => FEATURE_HTML4_REC_RESERVED, |
| 4272 |
datasrc => FEATURE_HTML4_REC_RESERVED, |
| 4273 |
declare => FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 4274 |
height => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4275 |
hspace => FEATURE_XHTML10_REC, |
| 4276 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 4277 |
name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4278 |
standby => FEATURE_M12N10_REC, |
| 4279 |
tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4280 |
type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4281 |
usemap => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4282 |
vspace => FEATURE_XHTML10_REC, |
| 4283 |
width => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4284 |
})->($self, $item, $element_state); |
| 4285 |
unless ($item->{node}->has_attribute_ns (undef, 'data')) { |
| 4286 |
unless ($item->{node}->has_attribute_ns (undef, 'type')) { |
| 4287 |
$self->{onerror}->(node => $item->{node}, |
| 4288 |
type => 'attribute missing:data|type', |
| 4289 |
level => $self->{level}->{must}); |
| 4290 |
} |
| 4291 |
} |
| 4292 |
|
| 4293 |
$element_state->{uri_info}->{data}->{type}->{embedded} = 1; |
| 4294 |
$element_state->{uri_info}->{classid}->{type}->{embedded} = 1; |
| 4295 |
$element_state->{uri_info}->{codebase}->{type}->{base} = 1; |
| 4296 |
## TODO: archive |
| 4297 |
$element_state->{uri_info}->{datasrc}->{type}->{resource} = 1; |
| 4298 |
}, |
| 4299 |
## NOTE: param*, transparent (Flow) |
| 4300 |
check_child_element => sub { |
| 4301 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 4302 |
$child_is_transparent, $element_state) = @_; |
| 4303 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 4304 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 4305 |
$self->{onerror}->(node => $child_el, |
| 4306 |
type => 'element not allowed:minus', |
| 4307 |
level => $self->{level}->{must}); |
| 4308 |
$element_state->{has_non_legend} = 1; |
| 4309 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 4310 |
# |
| 4311 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'param') { |
| 4312 |
if ($element_state->{has_non_param}) { |
| 4313 |
$self->{onerror}->(node => $child_el, |
| 4314 |
type => 'element not allowed:flow', |
| 4315 |
level => $self->{level}->{must}); |
| 4316 |
} |
| 4317 |
} else { |
| 4318 |
$HTMLFlowContentChecker{check_child_element}->(@_); |
| 4319 |
$element_state->{has_non_param} = 1; |
| 4320 |
} |
| 4321 |
}, |
| 4322 |
check_child_text => sub { |
| 4323 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 4324 |
if ($has_significant) { |
| 4325 |
$element_state->{has_non_param} = 1; |
| 4326 |
} |
| 4327 |
}, |
| 4328 |
check_end => sub { |
| 4329 |
my ($self, $item, $element_state) = @_; |
| 4330 |
if ($element_state->{has_significant}) { |
| 4331 |
$item->{real_parent_state}->{has_significant} = 1; |
| 4332 |
} elsif ($item->{node}->manakai_parent_element) { |
| 4333 |
## NOTE: Transparent. |
| 4334 |
} else { |
| 4335 |
$self->{onerror}->(node => $item->{node}, |
| 4336 |
level => $self->{level}->{should}, |
| 4337 |
type => 'no significant content'); |
| 4338 |
} |
| 4339 |
}, |
| 4340 |
## TODO: Tests for <nest/> in <object/> |
| 4341 |
}; |
| 4342 |
## ISSUE: Is |<menu><object data><li>aa</li></object></menu>| conforming? |
| 4343 |
## What about |<section><object data><style scoped></style>x</object></section>|? |
| 4344 |
## |<section><ins></ins><object data><style scoped></style>x</object></section>|? |
| 4345 |
|
| 4346 |
$Element->{$HTML_NS}->{param} = { |
| 4347 |
%HTMLEmptyChecker, |
| 4348 |
status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 4349 |
check_attrs => sub { |
| 4350 |
my ($self, $item, $element_state) = @_; |
| 4351 |
$GetHTMLAttrsChecker->({ |
| 4352 |
name => sub { }, |
| 4353 |
type => $HTMLIMTAttrChecker, |
| 4354 |
value => sub { }, |
| 4355 |
valuetype => $GetHTMLEnumeratedAttrChecker->({ |
| 4356 |
data => 1, ref => 1, object => 1, |
| 4357 |
}), |
| 4358 |
}, { |
| 4359 |
%HTMLAttrStatus, |
| 4360 |
href => FEATURE_RDFA_PR, |
| 4361 |
id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4362 |
name => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 4363 |
type => FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 4364 |
value => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 4365 |
valuetype => FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 4366 |
})->(@_); |
| 4367 |
unless ($item->{node}->has_attribute_ns (undef, 'name')) { |
| 4368 |
$self->{onerror}->(node => $item->{node}, |
| 4369 |
type => 'attribute missing', |
| 4370 |
text => 'name', |
| 4371 |
level => $self->{level}->{must}); |
| 4372 |
} |
| 4373 |
unless ($item->{node}->has_attribute_ns (undef, 'value')) { |
| 4374 |
$self->{onerror}->(node => $item->{node}, |
| 4375 |
type => 'attribute missing', |
| 4376 |
text => 'value', |
| 4377 |
level => $self->{level}->{must}); |
| 4378 |
} |
| 4379 |
}, |
| 4380 |
}; |
| 4381 |
|
| 4382 |
$Element->{$HTML_NS}->{video} = { |
| 4383 |
%HTMLTransparentChecker, |
| 4384 |
status => FEATURE_HTML5_LC, |
| 4385 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 4386 |
src => $HTMLURIAttrChecker, |
| 4387 |
## TODO: start, loopstart, loopend, end |
| 4388 |
## ISSUE: they MUST be "value time offset"s. Value? |
| 4389 |
## ISSUE: playcount has no conformance creteria |
| 4390 |
autoplay => $GetHTMLBooleanAttrChecker->('autoplay'), |
| 4391 |
controls => $GetHTMLBooleanAttrChecker->('controls'), |
| 4392 |
poster => $HTMLURIAttrChecker, |
| 4393 |
height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet |
| 4394 |
width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet |
| 4395 |
}, { |
| 4396 |
%HTMLAttrStatus, |
| 4397 |
autoplay => FEATURE_HTML5_LC, |
| 4398 |
controls => FEATURE_HTML5_LC, |
| 4399 |
end => FEATURE_HTML5_LC, |
| 4400 |
height => FEATURE_HTML5_LC, |
| 4401 |
loopend => FEATURE_HTML5_LC, |
| 4402 |
loopstart => FEATURE_HTML5_LC, |
| 4403 |
playcount => FEATURE_HTML5_LC, |
| 4404 |
poster => FEATURE_HTML5_LC, |
| 4405 |
src => FEATURE_HTML5_LC, |
| 4406 |
start => FEATURE_HTML5_LC, |
| 4407 |
width => FEATURE_HTML5_LC, |
| 4408 |
}), |
| 4409 |
check_start => sub { |
| 4410 |
my ($self, $item, $element_state) = @_; |
| 4411 |
$element_state->{allow_source} |
| 4412 |
= not $item->{node}->has_attribute_ns (undef, 'src'); |
| 4413 |
$element_state->{has_source} ||= $element_state->{allow_source} * -1; |
| 4414 |
## NOTE: It might be set true by |check_element|. |
| 4415 |
|
| 4416 |
$element_state->{uri_info}->{src}->{type}->{embedded} = 1; |
| 4417 |
$element_state->{uri_info}->{poster}->{type}->{embedded} = 1; |
| 4418 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 4419 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 4420 |
}, |
| 4421 |
check_child_element => sub { |
| 4422 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 4423 |
$child_is_transparent, $element_state) = @_; |
| 4424 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 4425 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 4426 |
$self->{onerror}->(node => $child_el, |
| 4427 |
type => 'element not allowed:minus', |
| 4428 |
level => $self->{level}->{must}); |
| 4429 |
delete $element_state->{allow_source}; |
| 4430 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 4431 |
# |
| 4432 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'source') { |
| 4433 |
unless ($element_state->{allow_source}) { |
| 4434 |
$self->{onerror}->(node => $child_el, |
| 4435 |
type => 'element not allowed:flow', |
| 4436 |
level => $self->{level}->{must}); |
| 4437 |
} |
| 4438 |
$element_state->{has_source} = 1; |
| 4439 |
} else { |
| 4440 |
delete $element_state->{allow_source}; |
| 4441 |
$HTMLFlowContentChecker{check_child_element}->(@_); |
| 4442 |
} |
| 4443 |
}, |
| 4444 |
check_child_text => sub { |
| 4445 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 4446 |
if ($has_significant) { |
| 4447 |
delete $element_state->{allow_source}; |
| 4448 |
} |
| 4449 |
$HTMLFlowContentChecker{check_child_text}->(@_); |
| 4450 |
}, |
| 4451 |
check_end => sub { |
| 4452 |
my ($self, $item, $element_state) = @_; |
| 4453 |
if ($element_state->{has_source} == -1) { |
| 4454 |
$self->{onerror}->(node => $item->{node}, |
| 4455 |
type => 'child element missing', |
| 4456 |
text => 'source', |
| 4457 |
level => $self->{level}->{must}); |
| 4458 |
} |
| 4459 |
|
| 4460 |
$Element->{$HTML_NS}->{object}->{check_end}->(@_); |
| 4461 |
}, |
| 4462 |
}; |
| 4463 |
|
| 4464 |
$Element->{$HTML_NS}->{audio} = { |
| 4465 |
%{$Element->{$HTML_NS}->{video}}, |
| 4466 |
status => FEATURE_HTML5_LC, |
| 4467 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 4468 |
src => $HTMLURIAttrChecker, |
| 4469 |
## TODO: start, loopstart, loopend, end |
| 4470 |
## ISSUE: they MUST be "value time offset"s. Value? |
| 4471 |
## ISSUE: playcount has no conformance creteria |
| 4472 |
autoplay => $GetHTMLBooleanAttrChecker->('autoplay'), |
| 4473 |
controls => $GetHTMLBooleanAttrChecker->('controls'), |
| 4474 |
}, { |
| 4475 |
%HTMLAttrStatus, |
| 4476 |
autoplay => FEATURE_HTML5_LC, |
| 4477 |
controls => FEATURE_HTML5_LC, |
| 4478 |
end => FEATURE_HTML5_LC, |
| 4479 |
loopend => FEATURE_HTML5_LC, |
| 4480 |
loopstart => FEATURE_HTML5_LC, |
| 4481 |
playcount => FEATURE_HTML5_LC, |
| 4482 |
src => FEATURE_HTML5_LC, |
| 4483 |
start => FEATURE_HTML5_LC, |
| 4484 |
}), |
| 4485 |
}; |
| 4486 |
|
| 4487 |
$Element->{$HTML_NS}->{source} = { |
| 4488 |
%HTMLEmptyChecker, |
| 4489 |
status => FEATURE_HTML5_DEFAULT, |
| 4490 |
check_attrs => sub { |
| 4491 |
my ($self, $item, $element_state) = @_; |
| 4492 |
$GetHTMLAttrsChecker->({ |
| 4493 |
media => $HTMLMQAttrChecker, |
| 4494 |
pixelratio => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }), |
| 4495 |
src => $HTMLURIAttrChecker, ## ISSUE: Negative or zero pixelratio="" |
| 4496 |
type => $HTMLIMTAttrChecker, |
| 4497 |
}, { |
| 4498 |
%HTMLAttrStatus, |
| 4499 |
media => FEATURE_HTML5_DEFAULT, |
| 4500 |
pixelratio => FEATURE_HTML5_DEFAULT, |
| 4501 |
src => FEATURE_HTML5_DEFAULT, |
| 4502 |
type => FEATURE_HTML5_DEFAULT, |
| 4503 |
})->(@_); |
| 4504 |
unless ($item->{node}->has_attribute_ns (undef, 'src')) { |
| 4505 |
$self->{onerror}->(node => $item->{node}, |
| 4506 |
type => 'attribute missing', |
| 4507 |
text => 'src', |
| 4508 |
level => $self->{level}->{must}); |
| 4509 |
} |
| 4510 |
|
| 4511 |
$element_state->{uri_info}->{src}->{type}->{embedded} = 1; |
| 4512 |
}, |
| 4513 |
}; |
| 4514 |
|
| 4515 |
$Element->{$HTML_NS}->{canvas} = { |
| 4516 |
%HTMLTransparentChecker, |
| 4517 |
status => FEATURE_HTML5_COMPLETE, |
| 4518 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 4519 |
height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), |
| 4520 |
width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), |
| 4521 |
}, { |
| 4522 |
%HTMLAttrStatus, |
| 4523 |
height => FEATURE_HTML5_COMPLETE, |
| 4524 |
width => FEATURE_HTML5_COMPLETE, |
| 4525 |
}), |
| 4526 |
}; |
| 4527 |
|
| 4528 |
$Element->{$HTML_NS}->{map} = { |
| 4529 |
%HTMLFlowContentChecker, |
| 4530 |
status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4531 |
check_attrs => sub { |
| 4532 |
my ($self, $item, $element_state) = @_; |
| 4533 |
my $has_name; |
| 4534 |
$GetHTMLAttrsChecker->({ |
| 4535 |
name => sub { |
| 4536 |
my ($self, $attr) = @_; |
| 4537 |
my $value = $attr->value; |
| 4538 |
if (length $value) { |
| 4539 |
## NOTE: Duplication is not non-conforming. |
| 4540 |
## NOTE: Space characters are not non-conforming. |
| 4541 |
# |
| 4542 |
} else { |
| 4543 |
$self->{onerror}->(node => $attr, |
| 4544 |
type => 'empty attribute value', |
| 4545 |
level => $self->{level}->{must}); |
| 4546 |
} |
| 4547 |
$self->{map}->{$value} ||= $attr; |
| 4548 |
$has_name = [$value, $attr]; |
| 4549 |
}, |
| 4550 |
}, { |
| 4551 |
%HTMLAttrStatus, |
| 4552 |
class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4553 |
dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4554 |
id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4555 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 4556 |
#name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED, |
| 4557 |
name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4558 |
onclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4559 |
ondblclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4560 |
onmousedown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4561 |
onmouseup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4562 |
onmouseover => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4563 |
onmousemove => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4564 |
onmouseout => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4565 |
onkeypress => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4566 |
onkeydown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4567 |
onkeyup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4568 |
title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4569 |
})->(@_); |
| 4570 |
|
| 4571 |
if ($has_name) { |
| 4572 |
my $id = $item->{node}->get_attribute_ns (undef, 'id'); |
| 4573 |
if (defined $id and $has_name->[0] ne $id) { |
| 4574 |
$self->{onerror}->(node => $item->{node}->get_attribute_node ('id'), |
| 4575 |
type => 'id ne name', |
| 4576 |
level => $self->{level}->{must}); |
| 4577 |
} |
| 4578 |
} else { |
| 4579 |
$self->{onerror}->(node => $item->{node}, |
| 4580 |
type => 'attribute missing', |
| 4581 |
text => 'name', |
| 4582 |
level => $self->{level}->{must}); |
| 4583 |
} |
| 4584 |
}, |
| 4585 |
check_start => sub { |
| 4586 |
my ($self, $item, $element_state) = @_; |
| 4587 |
$element_state->{in_map_original} = $self->{flag}->{in_map}; |
| 4588 |
$self->{flag}->{in_map} = [@{$self->{flag}->{in_map} or []}, {}]; |
| 4589 |
## NOTE: |{in_map}| is a reference to the array which contains |
| 4590 |
## hash references. Hashes are corresponding to the opening |
| 4591 |
## |map| elements and each of them contains the key-value |
| 4592 |
## pairs corresponding to the absolute URLs for the processed |
| 4593 |
## |area| elements in the |map| element corresponding to the |
| 4594 |
## hash. The key represents the resource (## TODO: use |
| 4595 |
## absolute URL), while the value represents whether there is |
| 4596 |
## an |area| element whose |alt| attribute is specified to a |
| 4597 |
## non-empty value. If there IS such an |area| element for |
| 4598 |
## the resource specified by the key, then the value is set to |
| 4599 |
## zero (|0|). Otherwise, if there is no such an |area| |
| 4600 |
## element but there is any |area| element with the empty |
| 4601 |
## |alt=""| attribute, then the value contains an array |
| 4602 |
## reference that contains all of such |area| elements. |
| 4603 |
|
| 4604 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 4605 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 4606 |
}, |
| 4607 |
check_end => sub { |
| 4608 |
my ($self, $item, $element_state) = @_; |
| 4609 |
|
| 4610 |
for (keys %{$self->{flag}->{in_map}->[-1]}) { |
| 4611 |
my $nodes = $self->{flag}->{in_map}->[-1]->{$_}; |
| 4612 |
next unless $nodes; |
| 4613 |
for (@$nodes) { |
| 4614 |
$self->{onerror}->(type => 'empty area alt', |
| 4615 |
node => $_, |
| 4616 |
level => $self->{level}->{html5_no_may}); |
| 4617 |
} |
| 4618 |
} |
| 4619 |
|
| 4620 |
$self->{flag}->{in_map} = $element_state->{in_map_original}; |
| 4621 |
|
| 4622 |
$HTMLFlowContentChecker{check_end}->(@_); |
| 4623 |
}, |
| 4624 |
}; |
| 4625 |
|
| 4626 |
$Element->{$HTML_NS}->{area} = { |
| 4627 |
%HTMLEmptyChecker, |
| 4628 |
status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4629 |
check_attrs => sub { |
| 4630 |
my ($self, $item, $element_state) = @_; |
| 4631 |
my %attr; |
| 4632 |
my $coords; |
| 4633 |
for my $attr (@{$item->{node}->attributes}) { |
| 4634 |
my $attr_ns = $attr->namespace_uri; |
| 4635 |
$attr_ns = '' unless defined $attr_ns; |
| 4636 |
my $attr_ln = $attr->manakai_local_name; |
| 4637 |
my $checker; |
| 4638 |
my $status; |
| 4639 |
if ($attr_ns eq '') { |
| 4640 |
$status = { |
| 4641 |
%HTMLAttrStatus, |
| 4642 |
%HTMLM12NCommonAttrStatus, |
| 4643 |
accesskey => FEATURE_M12N10_REC, |
| 4644 |
alt => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4645 |
coords => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4646 |
href => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_CR | FEATURE_M12N10_REC, |
| 4647 |
hreflang => FEATURE_HTML5_DEFAULT, |
| 4648 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 4649 |
media => FEATURE_HTML5_DEFAULT, |
| 4650 |
nohref => FEATURE_M12N10_REC, |
| 4651 |
onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4652 |
onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4653 |
ping => FEATURE_HTML5_DEFAULT, |
| 4654 |
rel => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_CR, |
| 4655 |
shape => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4656 |
tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4657 |
target => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 4658 |
type => FEATURE_HTML5_DEFAULT, |
| 4659 |
}->{$attr_ln}; |
| 4660 |
|
| 4661 |
$checker = { |
| 4662 |
accesskey => $HTMLAccesskeyAttrChecker, |
| 4663 |
alt => sub { }, |
| 4664 |
## NOTE: |alt| value has no conformance creteria. |
| 4665 |
shape => $GetHTMLEnumeratedAttrChecker->({ |
| 4666 |
circ => -1, circle => 1, |
| 4667 |
default => 1, |
| 4668 |
poly => 1, polygon => -1, |
| 4669 |
rect => 1, rectangle => -1, |
| 4670 |
}), |
| 4671 |
coords => sub { |
| 4672 |
my ($self, $attr) = @_; |
| 4673 |
my $value = $attr->value; |
| 4674 |
if ($value =~ /\A-?[0-9]+(?>,-?[0-9]+)*\z/) { |
| 4675 |
$coords = [split /,/, $value]; |
| 4676 |
} else { |
| 4677 |
$self->{onerror}->(node => $attr, |
| 4678 |
type => 'coords:syntax error', |
| 4679 |
level => $self->{level}->{must}); |
| 4680 |
} |
| 4681 |
}, |
| 4682 |
nohref => $GetHTMLBooleanAttrChecker->('nohref'), |
| 4683 |
target => $HTMLTargetAttrChecker, |
| 4684 |
href => $HTMLURIAttrChecker, |
| 4685 |
ping => $HTMLSpaceURIsAttrChecker, |
| 4686 |
rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) }, |
| 4687 |
media => $HTMLMQAttrChecker, |
| 4688 |
hreflang => $HTMLLanguageTagAttrChecker, |
| 4689 |
type => $HTMLIMTAttrChecker, |
| 4690 |
}->{$attr_ln}; |
| 4691 |
if ($checker) { |
| 4692 |
$attr{$attr_ln} = $attr; |
| 4693 |
} elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and |
| 4694 |
$attr_ln !~ /[A-Z]/) { |
| 4695 |
$checker = $HTMLDatasetAttrChecker; |
| 4696 |
$status = $HTMLDatasetAttrStatus; |
| 4697 |
} else { |
| 4698 |
$checker = $HTMLAttrChecker->{$attr_ln}; |
| 4699 |
} |
| 4700 |
} |
| 4701 |
$checker ||= $AttrChecker->{$attr_ns}->{$attr_ln} |
| 4702 |
|| $AttrChecker->{$attr_ns}->{''}; |
| 4703 |
$status ||= $AttrStatus->{$attr_ns}->{$attr_ln} |
| 4704 |
|| $AttrStatus->{$attr_ns}->{''}; |
| 4705 |
$status = FEATURE_ALLOWED if not defined $status and length $attr_ns; |
| 4706 |
|
| 4707 |
if ($checker) { |
| 4708 |
$checker->($self, $attr, $item, $element_state) if ref $checker; |
| 4709 |
} elsif ($attr_ns eq '' and not $status) { |
| 4710 |
# |
| 4711 |
} else { |
| 4712 |
$self->{onerror}->(node => $attr, |
| 4713 |
type => 'unknown attribute', |
| 4714 |
level => $self->{level}->{uncertain}); |
| 4715 |
## ISSUE: No comformance createria for unknown attributes in the spec |
| 4716 |
} |
| 4717 |
|
| 4718 |
$self->_attr_status_info ($attr, $status); |
| 4719 |
} |
| 4720 |
|
| 4721 |
if (defined $attr{href}) { |
| 4722 |
$self->{has_hyperlink_element} = 1; |
| 4723 |
if (defined $attr{alt}) { |
| 4724 |
my $url = $attr{href}->value; ## TODO: resolve |
| 4725 |
if (length $attr{alt}->value) { |
| 4726 |
for (@{$self->{flag}->{in_map} or []}) { |
| 4727 |
$_->{$url} = 0; |
| 4728 |
} |
| 4729 |
} else { |
| 4730 |
## NOTE: Empty |alt=""|. If there is another |area| element |
| 4731 |
## with the same |href=""| and that |area| elemnet's |
| 4732 |
## |alt=""| attribute is not an empty string, then this |
| 4733 |
## is conforming. |
| 4734 |
for (@{$self->{flag}->{in_map} or []}) { |
| 4735 |
push @{$_->{$url} ||= []}, $attr{alt} |
| 4736 |
unless exists $_->{$url} and not $_->{$url}; |
| 4737 |
} |
| 4738 |
} |
| 4739 |
} else { |
| 4740 |
$self->{onerror}->(node => $item->{node}, |
| 4741 |
type => 'attribute missing', |
| 4742 |
text => 'alt', |
| 4743 |
level => $self->{level}->{must}); |
| 4744 |
} |
| 4745 |
} else { |
| 4746 |
for (qw/target ping rel media hreflang type alt/) { |
| 4747 |
if (defined $attr{$_}) { |
| 4748 |
$self->{onerror}->(node => $attr{$_}, |
| 4749 |
type => 'attribute not allowed', |
| 4750 |
level => $self->{level}->{must}); |
| 4751 |
} |
| 4752 |
} |
| 4753 |
} |
| 4754 |
|
| 4755 |
my $shape = 'rectangle'; |
| 4756 |
if (defined $attr{shape}) { |
| 4757 |
$shape = { |
| 4758 |
circ => 'circle', circle => 'circle', |
| 4759 |
default => 'default', |
| 4760 |
poly => 'polygon', polygon => 'polygon', |
| 4761 |
rect => 'rectangle', rectangle => 'rectangle', |
| 4762 |
}->{lc $attr{shape}->value} || 'rectangle'; |
| 4763 |
## TODO: ASCII lowercase? |
| 4764 |
} |
| 4765 |
|
| 4766 |
if ($shape eq 'circle') { |
| 4767 |
if (defined $attr{coords}) { |
| 4768 |
if (defined $coords) { |
| 4769 |
if (@$coords == 3) { |
| 4770 |
if ($coords->[2] < 0) { |
| 4771 |
$self->{onerror}->(node => $attr{coords}, |
| 4772 |
type => 'coords:out of range', |
| 4773 |
index => 2, |
| 4774 |
value => $coords->[2], |
| 4775 |
level => $self->{level}->{must}); |
| 4776 |
} |
| 4777 |
} else { |
| 4778 |
$self->{onerror}->(node => $attr{coords}, |
| 4779 |
type => 'coords:number not 3', |
| 4780 |
text => 0+@$coords, |
| 4781 |
level => $self->{level}->{must}); |
| 4782 |
} |
| 4783 |
} else { |
| 4784 |
## NOTE: A syntax error has been reported. |
| 4785 |
} |
| 4786 |
} else { |
| 4787 |
$self->{onerror}->(node => $item->{node}, |
| 4788 |
type => 'attribute missing', |
| 4789 |
text => 'coords', |
| 4790 |
level => $self->{level}->{must}); |
| 4791 |
} |
| 4792 |
} elsif ($shape eq 'default') { |
| 4793 |
if (defined $attr{coords}) { |
| 4794 |
$self->{onerror}->(node => $attr{coords}, |
| 4795 |
type => 'attribute not allowed', |
| 4796 |
level => $self->{level}->{must}); |
| 4797 |
} |
| 4798 |
} elsif ($shape eq 'polygon') { |
| 4799 |
if (defined $attr{coords}) { |
| 4800 |
if (defined $coords) { |
| 4801 |
if (@$coords >= 6) { |
| 4802 |
unless (@$coords % 2 == 0) { |
| 4803 |
$self->{onerror}->(node => $attr{coords}, |
| 4804 |
type => 'coords:number not even', |
| 4805 |
text => 0+@$coords, |
| 4806 |
level => $self->{level}->{must}); |
| 4807 |
} |
| 4808 |
} else { |
| 4809 |
$self->{onerror}->(node => $attr{coords}, |
| 4810 |
type => 'coords:number lt 6', |
| 4811 |
text => 0+@$coords, |
| 4812 |
level => $self->{level}->{must}); |
| 4813 |
} |
| 4814 |
} else { |
| 4815 |
## NOTE: A syntax error has been reported. |
| 4816 |
} |
| 4817 |
} else { |
| 4818 |
$self->{onerror}->(node => $item->{node}, |
| 4819 |
type => 'attribute missing', |
| 4820 |
text => 'coords', |
| 4821 |
level => $self->{level}->{must}); |
| 4822 |
} |
| 4823 |
} elsif ($shape eq 'rectangle') { |
| 4824 |
if (defined $attr{coords}) { |
| 4825 |
if (defined $coords) { |
| 4826 |
if (@$coords == 4) { |
| 4827 |
unless ($coords->[0] < $coords->[2]) { |
| 4828 |
$self->{onerror}->(node => $attr{coords}, |
| 4829 |
type => 'coords:out of range', |
| 4830 |
index => 0, |
| 4831 |
value => $coords->[0], |
| 4832 |
level => $self->{level}->{must}); |
| 4833 |
} |
| 4834 |
unless ($coords->[1] < $coords->[3]) { |
| 4835 |
$self->{onerror}->(node => $attr{coords}, |
| 4836 |
type => 'coords:out of range', |
| 4837 |
index => 1, |
| 4838 |
value => $coords->[1], |
| 4839 |
level => $self->{level}->{must}); |
| 4840 |
} |
| 4841 |
} else { |
| 4842 |
$self->{onerror}->(node => $attr{coords}, |
| 4843 |
type => 'coords:number not 4', |
| 4844 |
text => 0+@$coords, |
| 4845 |
level => $self->{level}->{must}); |
| 4846 |
} |
| 4847 |
} else { |
| 4848 |
## NOTE: A syntax error has been reported. |
| 4849 |
} |
| 4850 |
} else { |
| 4851 |
$self->{onerror}->(node => $item->{node}, |
| 4852 |
type => 'attribute missing', |
| 4853 |
text => 'coords', |
| 4854 |
level => $self->{level}->{must}); |
| 4855 |
} |
| 4856 |
} |
| 4857 |
|
| 4858 |
$element_state->{uri_info}->{href}->{type}->{hyperlink} = 1; |
| 4859 |
}, |
| 4860 |
check_start => sub { |
| 4861 |
my ($self, $item, $element_state) = @_; |
| 4862 |
unless ($self->{flag}->{in_map} or |
| 4863 |
not $item->{node}->manakai_parent_element) { |
| 4864 |
$self->{onerror}->(node => $item->{node}, |
| 4865 |
type => 'element not allowed:area', |
| 4866 |
level => $self->{level}->{must}); |
| 4867 |
} |
| 4868 |
|
| 4869 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 4870 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 4871 |
}, |
| 4872 |
}; |
| 4873 |
|
| 4874 |
$Element->{$HTML_NS}->{table} = { |
| 4875 |
%HTMLChecker, |
| 4876 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 4877 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 4878 |
cellpadding => $HTMLLengthAttrChecker, |
| 4879 |
cellspacing => $HTMLLengthAttrChecker, |
| 4880 |
frame => $GetHTMLEnumeratedAttrChecker->({ |
| 4881 |
void => 1, above => 1, below => 1, hsides => 1, vsides => 1, |
| 4882 |
lhs => 1, rhs => 1, box => 1, border => 1, |
| 4883 |
}), |
| 4884 |
rules => $GetHTMLEnumeratedAttrChecker->({ |
| 4885 |
none => 1, groups => 1, rows => 1, cols => 1, all => 1, |
| 4886 |
}), |
| 4887 |
summary => sub {}, ## NOTE: %Text; in HTML4. |
| 4888 |
width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## %Pixels; |
| 4889 |
}, { |
| 4890 |
%HTMLAttrStatus, |
| 4891 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 4892 |
align => FEATURE_M12N10_REC_DEPRECATED, |
| 4893 |
bgcolor => FEATURE_M12N10_REC_DEPRECATED, |
| 4894 |
border => FEATURE_M12N10_REC, |
| 4895 |
cellpadding => FEATURE_M12N10_REC, |
| 4896 |
cellspacing => FEATURE_M12N10_REC, |
| 4897 |
cols => FEATURE_RFC1942, |
| 4898 |
datafld => FEATURE_HTML4_REC_RESERVED, |
| 4899 |
dataformatas => FEATURE_HTML4_REC_RESERVED, |
| 4900 |
datapagesize => FEATURE_M12N10_REC, |
| 4901 |
datasrc => FEATURE_HTML4_REC_RESERVED, |
| 4902 |
frame => FEATURE_M12N10_REC, |
| 4903 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 4904 |
rules => FEATURE_M12N10_REC, |
| 4905 |
summary => FEATURE_M12N10_REC, |
| 4906 |
width => FEATURE_M12N10_REC, |
| 4907 |
}), |
| 4908 |
check_start => sub { |
| 4909 |
my ($self, $item, $element_state) = @_; |
| 4910 |
$element_state->{phase} = 'before caption'; |
| 4911 |
|
| 4912 |
$element_state->{uri_info}->{datasrc}->{type}->{resource} = 1; |
| 4913 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 4914 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 4915 |
}, |
| 4916 |
check_child_element => sub { |
| 4917 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 4918 |
$child_is_transparent, $element_state) = @_; |
| 4919 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 4920 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 4921 |
$self->{onerror}->(node => $child_el, |
| 4922 |
type => 'element not allowed:minus', |
| 4923 |
level => $self->{level}->{must}); |
| 4924 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 4925 |
# |
| 4926 |
} elsif ($element_state->{phase} eq 'in tbodys') { |
| 4927 |
if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') { |
| 4928 |
#$element_state->{phase} = 'in tbodys'; |
| 4929 |
} elsif (not $element_state->{has_tfoot} and |
| 4930 |
$child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') { |
| 4931 |
$element_state->{phase} = 'after tfoot'; |
| 4932 |
$element_state->{has_tfoot} = 1; |
| 4933 |
} else { |
| 4934 |
$self->{onerror}->(node => $child_el, type => 'element not allowed', |
| 4935 |
level => $self->{level}->{must}); |
| 4936 |
} |
| 4937 |
} elsif ($element_state->{phase} eq 'in trs') { |
| 4938 |
if ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') { |
| 4939 |
#$element_state->{phase} = 'in trs'; |
| 4940 |
} elsif (not $element_state->{has_tfoot} and |
| 4941 |
$child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') { |
| 4942 |
$element_state->{phase} = 'after tfoot'; |
| 4943 |
$element_state->{has_tfoot} = 1; |
| 4944 |
} else { |
| 4945 |
$self->{onerror}->(node => $child_el, type => 'element not allowed', |
| 4946 |
level => $self->{level}->{must}); |
| 4947 |
} |
| 4948 |
} elsif ($element_state->{phase} eq 'after thead') { |
| 4949 |
if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') { |
| 4950 |
$element_state->{phase} = 'in tbodys'; |
| 4951 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') { |
| 4952 |
$element_state->{phase} = 'in trs'; |
| 4953 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') { |
| 4954 |
$element_state->{phase} = 'in tbodys'; |
| 4955 |
$element_state->{has_tfoot} = 1; |
| 4956 |
} else { |
| 4957 |
$self->{onerror}->(node => $child_el, type => 'element not allowed', |
| 4958 |
level => $self->{level}->{must}); |
| 4959 |
} |
| 4960 |
} elsif ($element_state->{phase} eq 'in colgroup') { |
| 4961 |
if ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') { |
| 4962 |
$element_state->{phase} = 'in colgroup'; |
| 4963 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') { |
| 4964 |
$element_state->{phase} = 'after thead'; |
| 4965 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') { |
| 4966 |
$element_state->{phase} = 'in tbodys'; |
| 4967 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') { |
| 4968 |
$element_state->{phase} = 'in trs'; |
| 4969 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') { |
| 4970 |
$element_state->{phase} = 'in tbodys'; |
| 4971 |
$element_state->{has_tfoot} = 1; |
| 4972 |
} else { |
| 4973 |
$self->{onerror}->(node => $child_el, type => 'element not allowed', |
| 4974 |
level => $self->{level}->{must}); |
| 4975 |
} |
| 4976 |
} elsif ($element_state->{phase} eq 'before caption') { |
| 4977 |
if ($child_nsuri eq $HTML_NS and $child_ln eq 'caption') { |
| 4978 |
$element_state->{phase} = 'in colgroup'; |
| 4979 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') { |
| 4980 |
$element_state->{phase} = 'in colgroup'; |
| 4981 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') { |
| 4982 |
$element_state->{phase} = 'after thead'; |
| 4983 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') { |
| 4984 |
$element_state->{phase} = 'in tbodys'; |
| 4985 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') { |
| 4986 |
$element_state->{phase} = 'in trs'; |
| 4987 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') { |
| 4988 |
$element_state->{phase} = 'in tbodys'; |
| 4989 |
$element_state->{has_tfoot} = 1; |
| 4990 |
} else { |
| 4991 |
$self->{onerror}->(node => $child_el, type => 'element not allowed', |
| 4992 |
level => $self->{level}->{must}); |
| 4993 |
} |
| 4994 |
} elsif ($element_state->{phase} eq 'after tfoot') { |
| 4995 |
$self->{onerror}->(node => $child_el, type => 'element not allowed', |
| 4996 |
level => $self->{level}->{must}); |
| 4997 |
} else { |
| 4998 |
die "check_child_element: Bad |table| phase: $element_state->{phase}"; |
| 4999 |
} |
| 5000 |
}, |
| 5001 |
check_child_text => sub { |
| 5002 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 5003 |
if ($has_significant) { |
| 5004 |
$self->{onerror}->(node => $child_node, type => 'character not allowed', |
| 5005 |
level => $self->{level}->{must}); |
| 5006 |
} |
| 5007 |
}, |
| 5008 |
check_end => sub { |
| 5009 |
my ($self, $item, $element_state) = @_; |
| 5010 |
|
| 5011 |
## Table model errors |
| 5012 |
require Whatpm::HTMLTable; |
| 5013 |
my $table = Whatpm::HTMLTable->form_table ($item->{node}, sub { |
| 5014 |
$self->{onerror}->(@_); |
| 5015 |
}, $self->{level}); |
| 5016 |
Whatpm::HTMLTable->assign_header |
| 5017 |
($table, $self->{onerror}, $self->{level}); |
| 5018 |
push @{$self->{return}->{table}}, $table; |
| 5019 |
|
| 5020 |
$HTMLChecker{check_end}->(@_); |
| 5021 |
}, |
| 5022 |
}; |
| 5023 |
|
| 5024 |
$Element->{$HTML_NS}->{caption} = { |
| 5025 |
%HTMLPhrasingContentChecker, |
| 5026 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 5027 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 5028 |
align => $GetHTMLEnumeratedAttrChecker->({ |
| 5029 |
top => 1, bottom => 1, left => 1, right => 1, |
| 5030 |
}), |
| 5031 |
}, { |
| 5032 |
%HTMLAttrStatus, |
| 5033 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 5034 |
align => FEATURE_M12N10_REC_DEPRECATED, |
| 5035 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 5036 |
}), |
| 5037 |
}; |
| 5038 |
|
| 5039 |
my %cellalign = ( |
| 5040 |
## HTML4 %cellhalign; |
| 5041 |
align => $GetHTMLEnumeratedAttrChecker->({ |
| 5042 |
left => 1, center => 1, right => 1, justify => 1, char => 1, |
| 5043 |
}), |
| 5044 |
char => sub { |
| 5045 |
my ($self, $attr) = @_; |
| 5046 |
|
| 5047 |
## NOTE: "character" or |%Character;| in HTML4. |
| 5048 |
|
| 5049 |
my $value = $attr->value; |
| 5050 |
if (length $value != 1) { |
| 5051 |
$self->{onerror}->(node => $attr, type => 'char:syntax error', |
| 5052 |
level => $self->{level}->{html4_fact}); |
| 5053 |
} |
| 5054 |
}, |
| 5055 |
charoff => $HTMLLengthAttrChecker, |
| 5056 |
|
| 5057 |
## HTML4 %cellvalign; |
| 5058 |
valign => $GetHTMLEnumeratedAttrChecker->({ |
| 5059 |
top => 1, middle => 1, bottom => 1, baseline => 1, |
| 5060 |
}), |
| 5061 |
); |
| 5062 |
|
| 5063 |
$Element->{$HTML_NS}->{colgroup} = { |
| 5064 |
%HTMLEmptyChecker, |
| 5065 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 5066 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 5067 |
%cellalign, |
| 5068 |
span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }), |
| 5069 |
## NOTE: Defined only if "the |colgroup| element contains no |col| elements" |
| 5070 |
## TODO: "attribute not supported" if |col|. |
| 5071 |
## ISSUE: MUST NOT if any |col|? |
| 5072 |
## ISSUE: MUST NOT for |<colgroup span="1"><any><col/></any></colgroup>| (though non-conforming)? |
| 5073 |
}, { |
| 5074 |
%HTMLAttrStatus, |
| 5075 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 5076 |
align => FEATURE_M12N10_REC, |
| 5077 |
char => FEATURE_M12N10_REC, |
| 5078 |
charoff => FEATURE_M12N10_REC, |
| 5079 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 5080 |
span => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 5081 |
valign => FEATURE_M12N10_REC, |
| 5082 |
width => FEATURE_M12N10_REC, |
| 5083 |
}), |
| 5084 |
check_child_element => sub { |
| 5085 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 5086 |
$child_is_transparent, $element_state) = @_; |
| 5087 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 5088 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 5089 |
$self->{onerror}->(node => $child_el, |
| 5090 |
type => 'element not allowed:minus', |
| 5091 |
level => $self->{level}->{must}); |
| 5092 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 5093 |
# |
| 5094 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'col') { |
| 5095 |
# |
| 5096 |
} else { |
| 5097 |
$self->{onerror}->(node => $child_el, type => 'element not allowed', |
| 5098 |
level => $self->{level}->{must}); |
| 5099 |
} |
| 5100 |
}, |
| 5101 |
check_child_text => sub { |
| 5102 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 5103 |
if ($has_significant) { |
| 5104 |
$self->{onerror}->(node => $child_node, type => 'character not allowed', |
| 5105 |
level => $self->{level}->{must}); |
| 5106 |
} |
| 5107 |
}, |
| 5108 |
}; |
| 5109 |
|
| 5110 |
$Element->{$HTML_NS}->{col} = { |
| 5111 |
%HTMLEmptyChecker, |
| 5112 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 5113 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 5114 |
%cellalign, |
| 5115 |
span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }), |
| 5116 |
}, { |
| 5117 |
%HTMLAttrStatus, |
| 5118 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 5119 |
align => FEATURE_M12N10_REC, |
| 5120 |
char => FEATURE_M12N10_REC, |
| 5121 |
charoff => FEATURE_M12N10_REC, |
| 5122 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 5123 |
span => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 5124 |
valign => FEATURE_M12N10_REC, |
| 5125 |
width => FEATURE_M12N10_REC, |
| 5126 |
}), |
| 5127 |
}; |
| 5128 |
|
| 5129 |
$Element->{$HTML_NS}->{tbody} = { |
| 5130 |
%HTMLChecker, |
| 5131 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 5132 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 5133 |
%cellalign, |
| 5134 |
}, { |
| 5135 |
%HTMLAttrStatus, |
| 5136 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 5137 |
align => FEATURE_M12N10_REC, |
| 5138 |
char => FEATURE_M12N10_REC, |
| 5139 |
charoff => FEATURE_M12N10_REC, |
| 5140 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 5141 |
valign => FEATURE_M12N10_REC, |
| 5142 |
}), |
| 5143 |
check_child_element => sub { |
| 5144 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 5145 |
$child_is_transparent, $element_state) = @_; |
| 5146 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 5147 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 5148 |
$self->{onerror}->(node => $child_el, |
| 5149 |
type => 'element not allowed:minus', |
| 5150 |
level => $self->{level}->{must}); |
| 5151 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 5152 |
# |
| 5153 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') { |
| 5154 |
# |
| 5155 |
} else { |
| 5156 |
$self->{onerror}->(node => $child_el, type => 'element not allowed', |
| 5157 |
level => $self->{level}->{must}); |
| 5158 |
} |
| 5159 |
}, |
| 5160 |
check_child_text => sub { |
| 5161 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 5162 |
if ($has_significant) { |
| 5163 |
$self->{onerror}->(node => $child_node, type => 'character not allowed', |
| 5164 |
level => $self->{level}->{must}); |
| 5165 |
} |
| 5166 |
}, |
| 5167 |
}; |
| 5168 |
|
| 5169 |
$Element->{$HTML_NS}->{thead} = { |
| 5170 |
%{$Element->{$HTML_NS}->{tbody}}, |
| 5171 |
}; |
| 5172 |
|
| 5173 |
$Element->{$HTML_NS}->{tfoot} = { |
| 5174 |
%{$Element->{$HTML_NS}->{tbody}}, |
| 5175 |
}; |
| 5176 |
|
| 5177 |
$Element->{$HTML_NS}->{tr} = { |
| 5178 |
%HTMLChecker, |
| 5179 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 5180 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 5181 |
%cellalign, |
| 5182 |
bgcolor => $HTMLColorAttrChecker, |
| 5183 |
}, { |
| 5184 |
%HTMLAttrStatus, |
| 5185 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 5186 |
align => FEATURE_M12N10_REC, |
| 5187 |
bgcolor => FEATURE_M12N10_REC_DEPRECATED, |
| 5188 |
char => FEATURE_M12N10_REC, |
| 5189 |
charoff => FEATURE_M12N10_REC, |
| 5190 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 5191 |
valign => FEATURE_M12N10_REC, |
| 5192 |
}), |
| 5193 |
check_child_element => sub { |
| 5194 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 5195 |
$child_is_transparent, $element_state) = @_; |
| 5196 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 5197 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 5198 |
$self->{onerror}->(node => $child_el, |
| 5199 |
type => 'element not allowed:minus', |
| 5200 |
level => $self->{level}->{must}); |
| 5201 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 5202 |
# |
| 5203 |
} elsif ($child_nsuri eq $HTML_NS and |
| 5204 |
($child_ln eq 'td' or $child_ln eq 'th')) { |
| 5205 |
# |
| 5206 |
} else { |
| 5207 |
$self->{onerror}->(node => $child_el, type => 'element not allowed', |
| 5208 |
level => $self->{level}->{must}); |
| 5209 |
} |
| 5210 |
}, |
| 5211 |
check_child_text => sub { |
| 5212 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 5213 |
if ($has_significant) { |
| 5214 |
$self->{onerror}->(node => $child_node, type => 'character not allowed', |
| 5215 |
level => $self->{level}->{must}); |
| 5216 |
} |
| 5217 |
}, |
| 5218 |
}; |
| 5219 |
|
| 5220 |
$Element->{$HTML_NS}->{td} = { |
| 5221 |
%HTMLFlowContentChecker, |
| 5222 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 5223 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 5224 |
%cellalign, |
| 5225 |
abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short. |
| 5226 |
axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated |
| 5227 |
bgcolor => $HTMLColorAttrChecker, |
| 5228 |
colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }), |
| 5229 |
headers => sub { |
| 5230 |
## NOTE: Will be checked by Whatpm::HTMLTable->assign_header. |
| 5231 |
## Though that method does not check the |headers| attribute of a |
| 5232 |
## |td| element if the element does not form a table, in that case |
| 5233 |
## the |td| element is non-conforming anyway. |
| 5234 |
}, |
| 5235 |
nowrap => $GetHTMLBooleanAttrChecker->('nowrap'), |
| 5236 |
rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }), |
| 5237 |
scope => $GetHTMLEnumeratedAttrChecker |
| 5238 |
->({row => 1, col => 1, rowgroup => 1, colgroup => 1}), |
| 5239 |
}, { |
| 5240 |
%HTMLAttrStatus, |
| 5241 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 5242 |
abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 5243 |
align => FEATURE_M12N10_REC, |
| 5244 |
axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 5245 |
bgcolor => FEATURE_M12N10_REC_DEPRECATED, |
| 5246 |
char => FEATURE_M12N10_REC, |
| 5247 |
charoff => FEATURE_M12N10_REC, |
| 5248 |
colspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 5249 |
headers => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 5250 |
height => FEATURE_M12N10_REC_DEPRECATED, |
| 5251 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 5252 |
nowrap => FEATURE_M12N10_REC_DEPRECATED, |
| 5253 |
rowspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 5254 |
scope => FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 5255 |
valign => FEATURE_M12N10_REC, |
| 5256 |
width => FEATURE_M12N10_REC_DEPRECATED, |
| 5257 |
}), |
| 5258 |
}; |
| 5259 |
|
| 5260 |
$Element->{$HTML_NS}->{th} = { |
| 5261 |
%HTMLPhrasingContentChecker, |
| 5262 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 5263 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 5264 |
%cellalign, |
| 5265 |
abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short. |
| 5266 |
axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated |
| 5267 |
bgcolor => $HTMLColorAttrChecker, |
| 5268 |
colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }), |
| 5269 |
## TODO: HTML4(?) |headers| |
| 5270 |
nowrap => $GetHTMLBooleanAttrChecker->('nowrap'), |
| 5271 |
rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }), |
| 5272 |
scope => $GetHTMLEnumeratedAttrChecker |
| 5273 |
->({row => 1, col => 1, rowgroup => 1, colgroup => 1}), |
| 5274 |
}, { |
| 5275 |
%HTMLAttrStatus, |
| 5276 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 5277 |
abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 5278 |
align => FEATURE_M12N10_REC, |
| 5279 |
axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 5280 |
bgcolor => FEATURE_M12N10_REC_DEPRECATED, |
| 5281 |
char => FEATURE_M12N10_REC, |
| 5282 |
charoff => FEATURE_M12N10_REC, |
| 5283 |
colspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 5284 |
headers => FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 5285 |
height => FEATURE_M12N10_REC_DEPRECATED, |
| 5286 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 5287 |
nowrap => FEATURE_M12N10_REC_DEPRECATED, |
| 5288 |
rowspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 5289 |
scope => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 5290 |
valign => FEATURE_M12N10_REC, |
| 5291 |
width => FEATURE_M12N10_REC_DEPRECATED, |
| 5292 |
}), |
| 5293 |
}; |
| 5294 |
|
| 5295 |
$Element->{$HTML_NS}->{form} = { |
| 5296 |
%HTMLFlowContentChecker, |
| 5297 |
status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC, |
| 5298 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 5299 |
accept => $AttrCheckerNotImplemented, ## TODO: ContentTypes [WF2] |
| 5300 |
'accept-charset' => $HTMLCharsetsAttrChecker, |
| 5301 |
action => $HTMLURIAttrChecker, ## TODO: "User agent behavior for a value other than HTTP URI is undefined" [HTML4] |
| 5302 |
data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2] |
| 5303 |
enctype => $HTMLIMTAttrChecker, ## TODO: "multipart/form-data" should be used when type=file is used [HTML4] ## TODO: MUST NOT parameter [WF2] |
| 5304 |
method => $GetHTMLEnumeratedAttrChecker->({ |
| 5305 |
get => 1, post => 1, put => 1, delete => 1, |
| 5306 |
}), |
| 5307 |
## NOTE: "get" SHOULD be used for idempotent submittion, |
| 5308 |
## "post" SHOULD be used otherwise [HTML4]. This cannot be tested. |
| 5309 |
name => sub { |
| 5310 |
my ($self, $attr) = @_; |
| 5311 |
|
| 5312 |
my $value = $attr->value; |
| 5313 |
if ($value eq '') { |
| 5314 |
$self->{onerror}->(type => 'empty form name', |
| 5315 |
node => $attr, |
| 5316 |
level => $self->{level}->{must}); |
| 5317 |
} else { |
| 5318 |
if ($self->{form}->{$value}) { |
| 5319 |
$self->{onerror}->(type => 'duplicate form name', |
| 5320 |
node => $attr, |
| 5321 |
value => $value, |
| 5322 |
level => $self->{level}->{must}); |
| 5323 |
} else { |
| 5324 |
$self->{form}->{$value} = 1; |
| 5325 |
} |
| 5326 |
} |
| 5327 |
}, |
| 5328 |
onformchange => $HTMLEventHandlerAttrChecker, |
| 5329 |
onforminput => $HTMLEventHandlerAttrChecker, |
| 5330 |
onreceived => $HTMLEventHandlerAttrChecker, |
| 5331 |
replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}), |
| 5332 |
target => $HTMLTargetAttrChecker, |
| 5333 |
## TODO: Warn for combination whose behavior is not defined in HTML4/WF2. |
| 5334 |
}, { |
| 5335 |
%HTMLAttrStatus, |
| 5336 |
%HTMLM12NCommonAttrStatus, |
| 5337 |
accept => FEATURE_WF2X | FEATURE_M12N10_REC, |
| 5338 |
'accept-charset' => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5339 |
action => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC, |
| 5340 |
data => FEATURE_WF2, |
| 5341 |
enctype => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC, |
| 5342 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 5343 |
method => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC, |
| 5344 |
#name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED, |
| 5345 |
name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5346 |
onformchange => FEATURE_WF2_INFORMATIVE, |
| 5347 |
onforminput => FEATURE_WF2_INFORMATIVE, |
| 5348 |
onreceived => FEATURE_WF2, |
| 5349 |
onreset => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5350 |
onsubmit => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5351 |
replace => FEATURE_WF2, |
| 5352 |
sdapref => FEATURE_HTML20_RFC, |
| 5353 |
sdasuff => FEATURE_HTML20_RFC, |
| 5354 |
target => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5355 |
}), |
| 5356 |
check_start => sub { |
| 5357 |
my ($self, $item, $element_state) = @_; |
| 5358 |
$self->_add_minus_elements ($element_state, {$HTML_NS => {form => 1}}); |
| 5359 |
|
| 5360 |
$element_state->{uri_info}->{action}->{type}->{action} = 1; |
| 5361 |
$element_state->{uri_info}->{data}->{type}->{resource} = 1; |
| 5362 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 5363 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 5364 |
$element_state->{id_type} = 'form'; |
| 5365 |
}, |
| 5366 |
check_end => sub { |
| 5367 |
my ($self, $item, $element_state) = @_; |
| 5368 |
$self->_remove_minus_elements ($element_state); |
| 5369 |
|
| 5370 |
$HTMLFlowContentChecker{check_end}->(@_); |
| 5371 |
}, |
| 5372 |
}; |
| 5373 |
|
| 5374 |
$Element->{$HTML_NS}->{fieldset} = { |
| 5375 |
%HTMLFlowContentChecker, |
| 5376 |
status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC, |
| 5377 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 5378 |
disabled => $GetHTMLBooleanAttrChecker->('disabled'), |
| 5379 |
form => $HTMLFormAttrChecker, |
| 5380 |
## TODO: name [HTML5] |
| 5381 |
}, { |
| 5382 |
%HTMLAttrStatus, |
| 5383 |
%HTMLM12NCommonAttrStatus, |
| 5384 |
disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 5385 |
form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 5386 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 5387 |
name => FEATURE_HTML5_DEFAULT, |
| 5388 |
}), |
| 5389 |
## NOTE: legend, Flow |
| 5390 |
check_child_element => sub { |
| 5391 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 5392 |
$child_is_transparent, $element_state) = @_; |
| 5393 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 5394 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 5395 |
$self->{onerror}->(node => $child_el, |
| 5396 |
type => 'element not allowed:minus', |
| 5397 |
level => $self->{level}->{must}); |
| 5398 |
$element_state->{has_non_legend} = 1; |
| 5399 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 5400 |
# |
| 5401 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') { |
| 5402 |
if ($element_state->{has_non_legend}) { |
| 5403 |
$self->{onerror}->(node => $child_el, |
| 5404 |
type => 'element not allowed:details legend', |
| 5405 |
level => $self->{level}->{must}); |
| 5406 |
} |
| 5407 |
$element_state->{has_legend} = 1; |
| 5408 |
$element_state->{has_non_legend} = 1; |
| 5409 |
} else { |
| 5410 |
$HTMLFlowContentChecker{check_child_element}->(@_); |
| 5411 |
$element_state->{has_non_legend} = 1 unless $child_is_transparent; |
| 5412 |
## TODO: |
| 5413 |
## |<details><object><legend>xx</legend></object>..</details>| |
| 5414 |
## should be an error, since |object| is allowed as flow, |
| 5415 |
## therefore |details| part of the content model does not match. |
| 5416 |
} |
| 5417 |
}, |
| 5418 |
check_child_text => sub { |
| 5419 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 5420 |
if ($has_significant) { |
| 5421 |
$element_state->{has_non_legend} = 1; |
| 5422 |
} |
| 5423 |
}, |
| 5424 |
check_end => sub { |
| 5425 |
my ($self, $item, $element_state) = @_; |
| 5426 |
|
| 5427 |
unless ($element_state->{has_legend}) { |
| 5428 |
$self->{onerror}->(node => $item->{node}, |
| 5429 |
type => 'child element missing', |
| 5430 |
text => 'legend', |
| 5431 |
level => $self->{level}->{must}); |
| 5432 |
} |
| 5433 |
|
| 5434 |
$HTMLFlowContentChecker{check_end}->(@_); |
| 5435 |
## ISSUE: |<details><legend>aa</legend></details>| error? |
| 5436 |
}, |
| 5437 |
## NOTE: This definition is partially reused by |details| element's |
| 5438 |
## checker. |
| 5439 |
## TODO: Tests for <nest/> in <fieldset> |
| 5440 |
}; |
| 5441 |
|
| 5442 |
$Element->{$HTML_NS}->{input} = { |
| 5443 |
%HTMLEmptyChecker, |
| 5444 |
status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC, |
| 5445 |
check_attrs => sub { |
| 5446 |
my ($self, $item, $element_state) = @_; |
| 5447 |
|
| 5448 |
my $state = $item->{node}->get_attribute_ns (undef, 'type'); |
| 5449 |
$state = 'text' unless defined $state; |
| 5450 |
$state =~ tr/A-Z/a-z/; ## ASCII case-insensitive |
| 5451 |
|
| 5452 |
for my $attr (@{$item->{node}->attributes}) { |
| 5453 |
my $attr_ns = $attr->namespace_uri; |
| 5454 |
$attr_ns = '' unless defined $attr_ns; |
| 5455 |
my $attr_ln = $attr->manakai_local_name; |
| 5456 |
my $checker; |
| 5457 |
my $status; |
| 5458 |
if ($attr_ns eq '') { |
| 5459 |
$status = |
| 5460 |
{ |
| 5461 |
%HTMLAttrStatus, |
| 5462 |
%HTMLM12NCommonAttrStatus, |
| 5463 |
accept => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC, |
| 5464 |
'accept-charset' => FEATURE_HTML2X_RFC, |
| 5465 |
accesskey => FEATURE_M12N10_REC, |
| 5466 |
action => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 5467 |
align => FEATURE_M12N10_REC_DEPRECATED, |
| 5468 |
alt => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5469 |
autocomplete => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 5470 |
autofocus => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 5471 |
checked => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5472 |
datafld => FEATURE_HTML4_REC_RESERVED, |
| 5473 |
dataformatas => FEATURE_HTML4_REC_RESERVED, |
| 5474 |
datasrc => FEATURE_HTML4_REC_RESERVED, |
| 5475 |
disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC, |
| 5476 |
enctype => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 5477 |
form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 5478 |
inputmode => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_XHTMLBASIC11_CR, |
| 5479 |
ismap => FEATURE_M12N10_REC, |
| 5480 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 5481 |
list => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 5482 |
max => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 5483 |
maxlength => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC, |
| 5484 |
method => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 5485 |
min => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 5486 |
name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5487 |
onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5488 |
onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5489 |
onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5490 |
onformchange => FEATURE_WF2_INFORMATIVE, |
| 5491 |
onforminput => FEATURE_WF2_INFORMATIVE, |
| 5492 |
oninput => FEATURE_WF2, |
| 5493 |
oninvalid => FEATURE_WF2, |
| 5494 |
onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5495 |
pattern => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 5496 |
readonly => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC, |
| 5497 |
replace => FEATURE_WF2, |
| 5498 |
required => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 5499 |
sdapref => FEATURE_HTML20_RFC, |
| 5500 |
size => FEATURE_HTML5_DEFAULT | FEATURE_WF2X_DEPRECATED | FEATURE_M12N10_REC, |
| 5501 |
src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5502 |
step => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 5503 |
tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5504 |
target => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 5505 |
template => FEATURE_HTML5_AT_RISK | FEATURE_WF2, |
| 5506 |
type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5507 |
usemap => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC, |
| 5508 |
value => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5509 |
}->{$attr_ln}; |
| 5510 |
|
| 5511 |
$checker = |
| 5512 |
{ |
| 5513 |
## NOTE: Value of an empty string means that the attribute is only |
| 5514 |
## applicable for a specific set of states. |
| 5515 |
accept => '', |
| 5516 |
'accept-charset' => $HTMLCharsetsAttrChecker, |
| 5517 |
## NOTE: To which states it applies is not defined in RFC 2070. |
| 5518 |
## TODO: tests |
| 5519 |
accesskey => $HTMLAccesskeyAttrChecker, |
| 5520 |
## NOTE: Not applied to |hidden| [WF2]. |
| 5521 |
## TODO: tests |
| 5522 |
action => '', |
| 5523 |
alt => '', |
| 5524 |
autocomplete => '', |
| 5525 |
autofocus => $GetHTMLBooleanAttrChecker->('autofocus'), |
| 5526 |
## ISSUE: No HTML5 definition yet. ## TODO: Tests |
| 5527 |
checked => '', |
| 5528 |
disabled => $GetHTMLBooleanAttrChecker->('disabled'), |
| 5529 |
## ISSUE: No HTML5 definition yet. ## TODO: Tests |
| 5530 |
enctype => '', |
| 5531 |
form => $HTMLFormAttrChecker, |
| 5532 |
ismap => '', ## NOTE: "MUST" [HTML4] |
| 5533 |
list => '', |
| 5534 |
max => '', |
| 5535 |
maxlength => '', |
| 5536 |
method => '', |
| 5537 |
min => '', |
| 5538 |
name => sub { |
| 5539 |
## ISSUE: No HTML5 definition yet. |
| 5540 |
## TODO: tests |
| 5541 |
}, |
| 5542 |
onformchange => $HTMLEventHandlerAttrChecker, # [WF2] |
| 5543 |
onforminput => $HTMLEventHandlerAttrChecker, # [WF2] |
| 5544 |
oninput => $HTMLEventHandlerAttrChecker, # [WF2] |
| 5545 |
oninvalid => $HTMLEventHandlerAttrChecker, # [WF2] |
| 5546 |
## TODO: tests for four attributes above |
| 5547 |
pattern => '', |
| 5548 |
readonly => '', |
| 5549 |
required => '', |
| 5550 |
size => '', |
| 5551 |
src => '', |
| 5552 |
step => '', |
| 5553 |
target => '', |
| 5554 |
type => $GetHTMLEnumeratedAttrChecker->({ |
| 5555 |
hidden => 1, text => 1, email => 1, url => 1, password => 1, |
| 5556 |
datetime => 1, date => 1, month => 1, week => 1, time => 1, |
| 5557 |
'datetime-local' => 1, number => 1, range => 1, checkbox => 1, |
| 5558 |
radio => 1, file => 1, submit => 1, image => 1, reset => 1, |
| 5559 |
button => 1, |
| 5560 |
}), |
| 5561 |
value => '', |
| 5562 |
}->{$attr_ln}; |
| 5563 |
|
| 5564 |
## State-dependent checkers |
| 5565 |
unless ($checker) { |
| 5566 |
if ($state eq 'hidden') { |
| 5567 |
$checker = |
| 5568 |
{ |
| 5569 |
value => sub { |
| 5570 |
my ($self, $attr, $item, $element_state) = @_; |
| 5571 |
my $name = $item->{node}->get_attribute_ns (undef, 'name'); |
| 5572 |
if (defined $name and $name eq '_charset_') { ## case-sensitive |
| 5573 |
$self->{onerror}->(node => $attr, |
| 5574 |
type => '_charset_ value', |
| 5575 |
level => $self->{level}->{must}); |
| 5576 |
} |
| 5577 |
}, |
| 5578 |
}->{$attr_ln} || $checker; |
| 5579 |
$checker = '' if $attr_ln eq 'accesskey'; |
| 5580 |
## TODO: Warn if no name attribute? |
| 5581 |
## TODO: Warn if name!=_charset_ and no value attribute? |
| 5582 |
} elsif ($state eq 'datetime') { |
| 5583 |
$checker = |
| 5584 |
{ |
| 5585 |
autocomplete => $GetHTMLEnumeratedAttrChecker->({ |
| 5586 |
on => 1, off => 1, |
| 5587 |
}), |
| 5588 |
## TODO: list |
| 5589 |
## TODO: max |
| 5590 |
## TODO: min |
| 5591 |
readonly => $GetHTMLBooleanAttrChecker->('readonly'), |
| 5592 |
required => $GetHTMLBooleanAttrChecker->('required'), |
| 5593 |
step => $StepAttrChecker, |
| 5594 |
value => sub { |
| 5595 |
## NOTE: No restriction. |
| 5596 |
## TODO: Warn if not a valid UTC date and time string. |
| 5597 |
}, |
| 5598 |
}->{$attr_ln} || $checker; |
| 5599 |
} elsif ($state eq 'date') { |
| 5600 |
$checker = |
| 5601 |
{ |
| 5602 |
autocomplete => $GetHTMLEnumeratedAttrChecker->({ |
| 5603 |
on => 1, off => 1, |
| 5604 |
}), |
| 5605 |
## TODO: list |
| 5606 |
## TODO: max |
| 5607 |
## TODO: min |
| 5608 |
readonly => $GetHTMLBooleanAttrChecker->('readonly'), |
| 5609 |
required => $GetHTMLBooleanAttrChecker->('required'), |
| 5610 |
step => $StepAttrChecker, |
| 5611 |
value => sub { |
| 5612 |
## NOTE: No restriction. |
| 5613 |
## TODO: Warn if not a valid date string. |
| 5614 |
}, |
| 5615 |
}->{$attr_ln} || $checker; |
| 5616 |
} elsif ($state eq 'month') { |
| 5617 |
$checker = |
| 5618 |
{ |
| 5619 |
autocomplete => $GetHTMLEnumeratedAttrChecker->({ |
| 5620 |
on => 1, off => 1, |
| 5621 |
}), |
| 5622 |
## TODO: list |
| 5623 |
## TODO: max |
| 5624 |
## TODO: min |
| 5625 |
readonly => $GetHTMLBooleanAttrChecker->('readonly'), |
| 5626 |
required => $GetHTMLBooleanAttrChecker->('required'), |
| 5627 |
step => $StepAttrChecker, |
| 5628 |
value => sub { |
| 5629 |
## NOTE: No restriction. |
| 5630 |
## TODO: Warn if not a valid month string. |
| 5631 |
}, |
| 5632 |
}->{$attr_ln} || $checker; |
| 5633 |
} elsif ($state eq 'week') { |
| 5634 |
$checker = |
| 5635 |
{ |
| 5636 |
autocomplete => $GetHTMLEnumeratedAttrChecker->({ |
| 5637 |
on => 1, off => 1, |
| 5638 |
}), |
| 5639 |
## TODO: list |
| 5640 |
## TODO: max |
| 5641 |
## TODO: min |
| 5642 |
readonly => $GetHTMLBooleanAttrChecker->('readonly'), |
| 5643 |
required => $GetHTMLBooleanAttrChecker->('required'), |
| 5644 |
step => $StepAttrChecker, |
| 5645 |
value => sub { |
| 5646 |
## NOTE: No restriction. |
| 5647 |
## TODO: Warn if not a valid week string. |
| 5648 |
}, |
| 5649 |
}->{$attr_ln} || $checker; |
| 5650 |
} elsif ($state eq 'time') { |
| 5651 |
$checker = |
| 5652 |
{ |
| 5653 |
autocomplete => $GetHTMLEnumeratedAttrChecker->({ |
| 5654 |
on => 1, off => 1, |
| 5655 |
}), |
| 5656 |
## TODO: list |
| 5657 |
## TODO: max |
| 5658 |
## TODO: min |
| 5659 |
readonly => $GetHTMLBooleanAttrChecker->('readonly'), |
| 5660 |
required => $GetHTMLBooleanAttrChecker->('required'), |
| 5661 |
step => $StepAttrChecker, |
| 5662 |
value => sub { |
| 5663 |
## NOTE: No restriction. |
| 5664 |
## TODO: Warn if not a valid time string. |
| 5665 |
}, |
| 5666 |
}->{$attr_ln} || $checker; |
| 5667 |
} elsif ($state eq 'datetime-local') { |
| 5668 |
$checker = |
| 5669 |
{ |
| 5670 |
autocomplete => $GetHTMLEnumeratedAttrChecker->({ |
| 5671 |
on => 1, off => 1, |
| 5672 |
}), |
| 5673 |
## TODO: list |
| 5674 |
## TODO: max |
| 5675 |
## TODO: min |
| 5676 |
readonly => $GetHTMLBooleanAttrChecker->('readonly'), |
| 5677 |
required => $GetHTMLBooleanAttrChecker->('required'), |
| 5678 |
step => $StepAttrChecker, |
| 5679 |
value => sub { |
| 5680 |
## NOTE: No restriction. |
| 5681 |
## TODO: Warn if not a valid local date and time string. |
| 5682 |
}, |
| 5683 |
}->{$attr_ln} || $checker; |
| 5684 |
} elsif ($state eq 'number') { |
| 5685 |
$checker = |
| 5686 |
{ |
| 5687 |
autocomplete => $GetHTMLEnumeratedAttrChecker->({ |
| 5688 |
on => 1, off => 1, |
| 5689 |
}), |
| 5690 |
## TODO: list |
| 5691 |
max => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }), |
| 5692 |
min => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }), |
| 5693 |
## TODO: min & max tests |
| 5694 |
readonly => $GetHTMLBooleanAttrChecker->('readonly'), |
| 5695 |
required => $GetHTMLBooleanAttrChecker->('required'), |
| 5696 |
step => $StepAttrChecker, |
| 5697 |
value => sub { |
| 5698 |
## NOTE: No restriction. |
| 5699 |
## TODO: Warn if not a valid floating point number. |
| 5700 |
}, |
| 5701 |
}->{$attr_ln} || $checker; |
| 5702 |
} elsif ($state eq 'range') { |
| 5703 |
$checker = |
| 5704 |
{ |
| 5705 |
autocomplete => $GetHTMLEnumeratedAttrChecker->({ |
| 5706 |
on => 1, off => 1, |
| 5707 |
}), |
| 5708 |
## TODO: list |
| 5709 |
max => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }), |
| 5710 |
min => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }), |
| 5711 |
## TODO: min & max tests |
| 5712 |
step => $StepAttrChecker, |
| 5713 |
value => sub { |
| 5714 |
## NOTE: No restriction. |
| 5715 |
## TODO: Warn if not a valid floating point number. |
| 5716 |
}, |
| 5717 |
}->{$attr_ln} || $checker; |
| 5718 |
} elsif ($state eq 'checkbox' or $state eq 'radio') { |
| 5719 |
$checker = |
| 5720 |
{ |
| 5721 |
checked => $GetHTMLBooleanAttrChecker->('checked'), |
| 5722 |
## ISSUE: checked value not (yet?) defined. |
| 5723 |
## TODO: tests |
| 5724 |
required => $GetHTMLBooleanAttrChecker->('required'), |
| 5725 |
value => sub { }, ## NOTE: No restriction. |
| 5726 |
}->{$attr_ln} || $checker; |
| 5727 |
## TODO: There MUST be another input type=radio with same |
| 5728 |
## name (Radio state). |
| 5729 |
## ISSUE: There should be exactly one type=radio with checked? |
| 5730 |
} elsif ($state eq 'file') { |
| 5731 |
$checker = |
| 5732 |
{ |
| 5733 |
## TODO: accept |
| 5734 |
required => $GetHTMLBooleanAttrChecker->('required'), |
| 5735 |
}->{$attr_ln} || $checker; |
| 5736 |
} elsif ($state eq 'submit') { |
| 5737 |
$checker = |
| 5738 |
{ |
| 5739 |
action => $HTMLURIAttrChecker, |
| 5740 |
## ISSUE: Not yet defined. |
| 5741 |
## TODO: tests |
| 5742 |
enctype => $HTMLIMTAttrChecker, |
| 5743 |
## ISSUE: Not yet defined. |
| 5744 |
## TODO: tests |
| 5745 |
method => $GetHTMLEnumeratedAttrChecker->({ |
| 5746 |
get => 1, post => 1, put => 1, delete => 1, |
| 5747 |
}), |
| 5748 |
## ISSUE: Not yet defined. |
| 5749 |
## TODO: tests |
| 5750 |
replace => $GetHTMLEnumeratedAttrChecker->({ |
| 5751 |
document => 1, values => 1, |
| 5752 |
}), |
| 5753 |
## TODO: tests |
| 5754 |
target => $HTMLTargetAttrChecker, |
| 5755 |
## ISSUE: Not yet defined. |
| 5756 |
## TODO: tests |
| 5757 |
value => sub { }, ## NOTE: No restriction. |
| 5758 |
}->{$attr_ln} || $checker; |
| 5759 |
} elsif ($state eq 'image') { |
| 5760 |
$checker = |
| 5761 |
{ |
| 5762 |
action => $HTMLURIAttrChecker, |
| 5763 |
## ISSUE: Not yet defined. |
| 5764 |
## TODO: tests |
| 5765 |
align => $GetHTMLEnumeratedAttrChecker->({ |
| 5766 |
top => 1, middle => 1, bottom => 1, left => 1, right => 1, |
| 5767 |
}), |
| 5768 |
## TODO: tests |
| 5769 |
alt => sub { |
| 5770 |
my ($self, $attr) = @_; |
| 5771 |
my $value = $attr->value; |
| 5772 |
unless (length $value) { |
| 5773 |
$self->{onerror}->(node => $attr, |
| 5774 |
type => 'empty anchor image alt', |
| 5775 |
level => $self->{level}->{must}); |
| 5776 |
} |
| 5777 |
}, |
| 5778 |
enctype => $HTMLIMTAttrChecker, |
| 5779 |
## ISSUE: Not yet defined. |
| 5780 |
## TODO: tests |
| 5781 |
ismap => $GetHTMLBooleanAttrChecker->('ismap'), |
| 5782 |
## TODO: tests |
| 5783 |
method => $GetHTMLEnumeratedAttrChecker->({ |
| 5784 |
get => 1, post => 1, put => 1, delete => 1, |
| 5785 |
}), |
| 5786 |
## ISSUE: Not yet defined. |
| 5787 |
## TODO: tests |
| 5788 |
replace => $GetHTMLEnumeratedAttrChecker->({ |
| 5789 |
document => 1, values => 1, |
| 5790 |
}), |
| 5791 |
## TODO: tests |
| 5792 |
src => $HTMLURIAttrChecker, |
| 5793 |
## TODO: There is requirements on the referenced resource. |
| 5794 |
## TODO: tests |
| 5795 |
target => $HTMLTargetAttrChecker, |
| 5796 |
## ISSUE: Not yet defined. |
| 5797 |
## TODO: tests |
| 5798 |
usemap => $HTMLUsemapAttrChecker, |
| 5799 |
## TODO: tests |
| 5800 |
}->{$attr_ln} || $checker; |
| 5801 |
## TODO: alt & src are required. |
| 5802 |
} elsif ({ |
| 5803 |
reset => 1, button => 1, |
| 5804 |
## NOTE: From Web Forms 2.0: |
| 5805 |
remove => 1, 'move-up' => 1, 'move-down' => 1, |
| 5806 |
add => 1, |
| 5807 |
}->{$state}) { |
| 5808 |
$checker = |
| 5809 |
{ |
| 5810 |
## NOTE: According to Web Forms 2.0, |input| attribute |
| 5811 |
## has |template| attribute to support the |add| button |
| 5812 |
## type (as part of the repetition template feature). It |
| 5813 |
## conflicts with the |template| global attribute |
| 5814 |
## introduced as part of the data template feature. |
| 5815 |
## NOTE: |template| attribute as defined in Web Forms 2.0 |
| 5816 |
## has no author requirement. |
| 5817 |
## TODO: template tests |
| 5818 |
value => sub { }, ## NOTE: No restriction. |
| 5819 |
}->{$attr_ln} || $checker; |
| 5820 |
} else { # Text, E-mail, URL, Password |
| 5821 |
$checker = |
| 5822 |
{ |
| 5823 |
autocomplete => $GetHTMLEnumeratedAttrChecker->({ |
| 5824 |
on => 1, off => 1, |
| 5825 |
}), |
| 5826 |
## TODO: inputmode [WF2] |
| 5827 |
## TODO: list |
| 5828 |
maxlength => sub { |
| 5829 |
my ($self, $attr, $item, $element_state) = @_; |
| 5830 |
|
| 5831 |
$GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 })->(@_); |
| 5832 |
|
| 5833 |
if ($attr->value =~ /^[\x09\x0A\x0D\x20]*([0-9]+)/) { |
| 5834 |
## NOTE: Applying the rules for parsing non-negative |
| 5835 |
## integers results in a number. |
| 5836 |
my $max_allowed_value_length = 0+$1; |
| 5837 |
|
| 5838 |
my $value = $item->{node}->get_attribute_ns (undef, 'value'); |
| 5839 |
if (defined $value) { |
| 5840 |
my $codepoint_length = length $value; |
| 5841 |
$codepoint_length++ |
| 5842 |
while $value =~ /[\x{10000}-\x{10FFFF}]/g; |
| 5843 |
|
| 5844 |
if ($codepoint_length > $max_allowed_value_length) { |
| 5845 |
$self->{onerror} |
| 5846 |
->(node => $item->{node} |
| 5847 |
->get_attribute_node_ns (undef, 'value'), |
| 5848 |
type => 'value too long', |
| 5849 |
level => $self->{level}->{must}); |
| 5850 |
} |
| 5851 |
} |
| 5852 |
} |
| 5853 |
}, |
| 5854 |
## TODO: pattern |
| 5855 |
readonly => $GetHTMLBooleanAttrChecker->('readonly'), |
| 5856 |
required => $GetHTMLBooleanAttrChecker->('required'), |
| 5857 |
size => $GetHTMLNonNegativeIntegerAttrChecker->(sub {shift > 0}), |
| 5858 |
value => sub { |
| 5859 |
## NOTE: No restriction. |
| 5860 |
## TODO: Warn if it contains CR or LF? |
| 5861 |
## TODO: Warn if not addr-spec (E-Mail state)? |
| 5862 |
## TODO: Warn if not a URL (URL state)? |
| 5863 |
}, |
| 5864 |
}->{$attr_ln} || $checker; |
| 5865 |
$checker = '' if $state eq 'password' and $attr_ln eq 'list'; |
| 5866 |
$state = 'text'; |
| 5867 |
} |
| 5868 |
} |
| 5869 |
|
| 5870 |
if (defined $checker) { |
| 5871 |
if ($checker eq '') { |
| 5872 |
$checker = sub { |
| 5873 |
my ($self, $attr) = @_; |
| 5874 |
$self->{onerror}->(node => $attr, |
| 5875 |
type => 'input attr not applicable', |
| 5876 |
text => $state, |
| 5877 |
level => $self->{level}->{must}); |
| 5878 |
}; |
| 5879 |
} |
| 5880 |
} elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and |
| 5881 |
$attr_ln !~ /[A-Z]/) { |
| 5882 |
$checker = $HTMLDatasetAttrChecker; |
| 5883 |
$status = $HTMLDatasetAttrStatus; |
| 5884 |
} else { |
| 5885 |
$checker = $HTMLAttrChecker->{$attr_ln}; |
| 5886 |
} |
| 5887 |
} |
| 5888 |
$checker ||= $AttrChecker->{$attr_ns}->{$attr_ln} |
| 5889 |
|| $AttrChecker->{$attr_ns}->{''}; |
| 5890 |
$status ||= $AttrStatus->{$attr_ns}->{$attr_ln} |
| 5891 |
|| $AttrStatus->{$attr_ns}->{''}; |
| 5892 |
$status = FEATURE_ALLOWED if not defined $status and length $attr_ns; |
| 5893 |
|
| 5894 |
if ($checker) { |
| 5895 |
$checker->($self, $attr, $item, $element_state) if ref $checker; |
| 5896 |
} elsif ($attr_ns eq '' and not $status) { |
| 5897 |
# |
| 5898 |
} else { |
| 5899 |
$self->{onerror}->(node => $attr, |
| 5900 |
type => 'unknown attribute', |
| 5901 |
level => $self->{level}->{uncertain}); |
| 5902 |
## ISSUE: No comformance createria for unknown attributes in the spec |
| 5903 |
} |
| 5904 |
|
| 5905 |
$self->_attr_status_info ($attr, $status); |
| 5906 |
} |
| 5907 |
}, |
| 5908 |
## TODO: Tests |
| 5909 |
## TODO: Tests for <nest/> in <input> |
| 5910 |
check_start => sub { |
| 5911 |
my ($self, $item, $element_state) = @_; |
| 5912 |
if ($self->{flag}->{has_label} and $self->{flag}->{has_labelable}) { |
| 5913 |
$self->{onerror}->(node => $item->{node}, |
| 5914 |
type => 'multiple labelable fae', |
| 5915 |
level => $self->{level}->{must}); |
| 5916 |
} else { |
| 5917 |
$self->{flag}->{has_labelable} = 2; |
| 5918 |
} |
| 5919 |
|
| 5920 |
$element_state->{uri_info}->{action}->{type}->{action} = 1; |
| 5921 |
$element_state->{uri_info}->{datasrc}->{type}->{resource} = 1; |
| 5922 |
$element_state->{uri_info}->{src}->{type}->{embedded} = 1; |
| 5923 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 5924 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 5925 |
|
| 5926 |
$element_state->{id_type} = 'labelable'; |
| 5927 |
}, |
| 5928 |
}; |
| 5929 |
|
| 5930 |
## TODO: Form |name| attributes: MUST NOT conflict with RFC 3106 [WF2] |
| 5931 |
|
| 5932 |
## NOTE: "authors who are nesting repetition blocks should position such |
| 5933 |
## [repetition-block-related] buttons carefully to make clear which block a |
| 5934 |
## button applies to.": I have no idea how this can be tested. |
| 5935 |
|
| 5936 |
$Element->{$HTML_NS}->{button} = { |
| 5937 |
%HTMLPhrasingContentChecker, ## ISSUE: -interactive? |
| 5938 |
status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC, |
| 5939 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 5940 |
accesskey => $HTMLAccesskeyAttrChecker, |
| 5941 |
action => $HTMLURIAttrChecker, |
| 5942 |
autofocus => $GetHTMLBooleanAttrChecker->('autofocus'), |
| 5943 |
disabled => $GetHTMLBooleanAttrChecker->('disabled'), |
| 5944 |
form => $HTMLFormAttrChecker, |
| 5945 |
method => $GetHTMLEnumeratedAttrChecker->({ |
| 5946 |
get => 1, post => 1, put => 1, delete => 1, |
| 5947 |
}), |
| 5948 |
name => sub {}, ## NOTE: CDATA [M12N] |
| 5949 |
onformchange => $HTMLEventHandlerAttrChecker, |
| 5950 |
onforminput => $HTMLEventHandlerAttrChecker, |
| 5951 |
replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}), |
| 5952 |
target => $HTMLTargetAttrChecker, |
| 5953 |
## NOTE: According to Web Forms 2.0, |button| attribute has |template| |
| 5954 |
## attribute to support the |add| button type (as part of repetition |
| 5955 |
## template feature). It conflicts with the |template| global attribute |
| 5956 |
## introduced as part of the data template feature. |
| 5957 |
## NOTE: |template| attribute as defined in Web Forms 2.0 has no |
| 5958 |
## author requirement. |
| 5959 |
type => $GetHTMLEnumeratedAttrChecker->({ |
| 5960 |
button => 1, submit => 1, reset => 1, |
| 5961 |
## [WF2] |
| 5962 |
add => 1, remove => 1, 'move-up' => 1, 'move-down' => 1, |
| 5963 |
}), |
| 5964 |
value => sub {}, ## NOTE: CDATA [M12N] |
| 5965 |
}, { |
| 5966 |
%HTMLAttrStatus, |
| 5967 |
%HTMLM12NCommonAttrStatus, |
| 5968 |
accesskey => FEATURE_M12N10_REC, |
| 5969 |
action => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 5970 |
autofocus => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 5971 |
datafld => FEATURE_HTML4_REC_RESERVED, |
| 5972 |
dataformatas => FEATURE_HTML4_REC_RESERVED, |
| 5973 |
datasrc => FEATURE_HTML4_REC_RESERVED, |
| 5974 |
disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC, |
| 5975 |
enctype => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 5976 |
form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 5977 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 5978 |
method => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 5979 |
name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5980 |
onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5981 |
onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5982 |
onformchange => FEATURE_WF2_INFORMATIVE, |
| 5983 |
onforminput => FEATURE_WF2_INFORMATIVE, |
| 5984 |
replace => FEATURE_WF2, |
| 5985 |
tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5986 |
target => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 5987 |
template => FEATURE_WF2, |
| 5988 |
type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5989 |
value => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 5990 |
}), |
| 5991 |
check_start => sub { |
| 5992 |
my ($self, $item, $element_state) = @_; |
| 5993 |
if ($self->{flag}->{has_label} and $self->{flag}->{has_labelable}) { |
| 5994 |
$self->{onerror}->(node => $item->{node}, |
| 5995 |
type => 'multiple labelable fae', |
| 5996 |
level => $self->{level}->{must}); |
| 5997 |
} else { |
| 5998 |
$self->{flag}->{has_labelable} = 2; |
| 5999 |
} |
| 6000 |
|
| 6001 |
$element_state->{uri_info}->{action}->{type}->{action} = 1; |
| 6002 |
$element_state->{uri_info}->{datasrc}->{type}->{resource} = 1; |
| 6003 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 6004 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 6005 |
|
| 6006 |
$element_state->{id_type} = 'labelable'; |
| 6007 |
}, |
| 6008 |
}; |
| 6009 |
|
| 6010 |
$Element->{$HTML_NS}->{label} = { |
| 6011 |
%HTMLPhrasingContentChecker, |
| 6012 |
status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC |
| 6013 |
| FEATURE_XHTML2_ED, |
| 6014 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 6015 |
accesskey => $HTMLAccesskeyAttrChecker, |
| 6016 |
for => sub { |
| 6017 |
my ($self, $attr) = @_; |
| 6018 |
|
| 6019 |
## NOTE: MUST be an ID of a labelable element. |
| 6020 |
|
| 6021 |
push @{$self->{idref}}, ['labelable', $attr->value, $attr]; |
| 6022 |
}, |
| 6023 |
form => $HTMLFormAttrChecker, |
| 6024 |
}, { |
| 6025 |
%HTMLAttrStatus, |
| 6026 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 6027 |
accesskey => FEATURE_WF2 | FEATURE_M12N10_REC, |
| 6028 |
for => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6029 |
form => FEATURE_HTML5_DEFAULT, |
| 6030 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 6031 |
onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6032 |
onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6033 |
}), |
| 6034 |
check_start => sub { |
| 6035 |
my ($self, $item, $element_state) = @_; |
| 6036 |
$self->_add_minus_elements ($element_state, {$HTML_NS => {label => 1}}); |
| 6037 |
|
| 6038 |
$element_state->{has_label_original} = $self->{flag}->{has_label}; |
| 6039 |
$self->{flag}->{has_label} = 1; |
| 6040 |
$element_state->{has_labelable_original} = $self->{flag}->{has_labelable}; |
| 6041 |
$self->{flag}->{has_labelable} = $item->{node}->has_attribute ('for')?1:0; |
| 6042 |
|
| 6043 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 6044 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 6045 |
}, |
| 6046 |
check_end => sub { |
| 6047 |
my ($self, $item, $element_state) = @_; |
| 6048 |
$self->_remove_minus_elements ($element_state); |
| 6049 |
|
| 6050 |
if ($self->{flag}->{has_labelable} == 1) { # has for="" but no labelable |
| 6051 |
$self->{flag}->{has_labelable} |
| 6052 |
= $element_state->{has_labelable_original}; |
| 6053 |
} |
| 6054 |
delete $self->{flag}->{has_label} |
| 6055 |
unless $element_state->{has_label_original}; |
| 6056 |
## TODO: Warn if no labelable descendant? <input type=hidden>? |
| 6057 |
|
| 6058 |
## NOTE: |<label for=a><input id=a></label>| is non-conforming. |
| 6059 |
|
| 6060 |
$HTMLPhrasingContentChecker{check_end}->(@_); |
| 6061 |
}, |
| 6062 |
## TODO: Tests for <nest/> in <label> |
| 6063 |
}; |
| 6064 |
|
| 6065 |
$Element->{$HTML_NS}->{select} = { |
| 6066 |
%HTMLChecker, |
| 6067 |
## TODO: author should SELECTED at least one OPTION in non-MULTIPLE case [HTML4]. |
| 6068 |
## TODO: more than one OPTION with SELECTED in non-MULTIPLE case is "error" [HTML4] |
| 6069 |
status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC, |
| 6070 |
is_root => 1, ## TODO: SHOULD NOT in application/xhtml+xml [WF2] |
| 6071 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 6072 |
accesskey => $HTMLAccesskeyAttrChecker, |
| 6073 |
autofocus => $GetHTMLBooleanAttrChecker->('autofocus'), |
| 6074 |
disabled => $GetHTMLBooleanAttrChecker->('disabled'), |
| 6075 |
data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2] |
| 6076 |
form => $HTMLFormAttrChecker, |
| 6077 |
multiple => $GetHTMLBooleanAttrChecker->('multiple'), |
| 6078 |
name => sub {}, ## NOTE: CDATA [M12N] |
| 6079 |
onformchange => $HTMLEventHandlerAttrChecker, |
| 6080 |
onforminput => $HTMLEventHandlerAttrChecker, |
| 6081 |
oninput => $HTMLEventHandlerAttrChecker, |
| 6082 |
oninvalid => $HTMLEventHandlerAttrChecker, |
| 6083 |
size => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), |
| 6084 |
}, { |
| 6085 |
%HTMLAttrStatus, |
| 6086 |
%HTMLM12NCommonAttrStatus, |
| 6087 |
accesskey => FEATURE_WF2, |
| 6088 |
autofocus => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 6089 |
data => FEATURE_WF2, |
| 6090 |
datafld => FEATURE_HTML4_REC_RESERVED, |
| 6091 |
dataformatas => FEATURE_HTML4_REC_RESERVED, |
| 6092 |
datasrc => FEATURE_HTML4_REC_RESERVED, |
| 6093 |
disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC, |
| 6094 |
form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 6095 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 6096 |
multiple => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6097 |
name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6098 |
onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6099 |
onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6100 |
onformchange => FEATURE_WF2_INFORMATIVE, |
| 6101 |
onforminput => FEATURE_WF2_INFORMATIVE, |
| 6102 |
onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6103 |
oninput => FEATURE_WF2, |
| 6104 |
oninvalid => FEATURE_WF2, |
| 6105 |
sdaform => FEATURE_HTML20_RFC, |
| 6106 |
sdapref => FEATURE_HTML20_RFC, |
| 6107 |
size => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6108 |
tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6109 |
}), |
| 6110 |
check_start => sub { |
| 6111 |
my ($self, $item, $element_state) = @_; |
| 6112 |
if ($self->{flag}->{has_label} and $self->{flag}->{has_labelable}) { |
| 6113 |
$self->{onerror}->(node => $item->{node}, |
| 6114 |
type => 'multiple labelable fae', |
| 6115 |
level => $self->{level}->{must}); |
| 6116 |
} else { |
| 6117 |
$self->{flag}->{has_labelable} = 2; |
| 6118 |
} |
| 6119 |
|
| 6120 |
$element_state->{uri_info}->{data}->{type}->{resource} = 1; |
| 6121 |
$element_state->{uri_info}->{datasrc}->{type}->{resource} = 1; |
| 6122 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 6123 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 6124 |
|
| 6125 |
$element_state->{id_type} = 'labelable'; |
| 6126 |
}, |
| 6127 |
check_child_element => sub { |
| 6128 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 6129 |
$child_is_transparent, $element_state) = @_; |
| 6130 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 6131 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 6132 |
$self->{onerror}->(node => $child_el, |
| 6133 |
type => 'element not allowed:minus', |
| 6134 |
level => $self->{level}->{must}); |
| 6135 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 6136 |
# |
| 6137 |
} elsif ($child_nsuri eq $HTML_NS and |
| 6138 |
{ |
| 6139 |
option => 1, optgroup => 1, |
| 6140 |
}->{$child_ln}) { |
| 6141 |
# |
| 6142 |
} else { |
| 6143 |
$self->{onerror}->(node => $child_el, type => 'element not allowed', |
| 6144 |
level => $self->{level}->{must}); |
| 6145 |
} |
| 6146 |
}, |
| 6147 |
check_child_text => sub { |
| 6148 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 6149 |
if ($has_significant) { |
| 6150 |
$self->{onerror}->(node => $child_node, type => 'character not allowed', |
| 6151 |
level => $self->{level}->{must}); |
| 6152 |
} |
| 6153 |
}, |
| 6154 |
}; |
| 6155 |
|
| 6156 |
$Element->{$HTML_NS}->{datalist} = { |
| 6157 |
%HTMLPhrasingContentChecker, |
| 6158 |
## TODO: |option| child MUST be empty [WF2] |
| 6159 |
status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 6160 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 6161 |
data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2] |
| 6162 |
}, { |
| 6163 |
%HTMLAttrStatus, |
| 6164 |
data => FEATURE_WF2, |
| 6165 |
}), |
| 6166 |
check_start => sub { |
| 6167 |
my ($self, $item, $element_state) = @_; |
| 6168 |
|
| 6169 |
$element_state->{phase} = 'any'; # any | phrasing | option |
| 6170 |
|
| 6171 |
$element_state->{uri_info}->{data}->{type}->{resource} = 1; |
| 6172 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 6173 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 6174 |
}, |
| 6175 |
## NOTE: phrasing | option* |
| 6176 |
check_child_element => sub { |
| 6177 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 6178 |
$child_is_transparent, $element_state) = @_; |
| 6179 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 6180 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 6181 |
$self->{onerror}->(node => $child_el, |
| 6182 |
type => 'element not allowed:minus', |
| 6183 |
level => $self->{level}->{must}); |
| 6184 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 6185 |
# |
| 6186 |
} elsif ($element_state->{phase} eq 'phrasing') { |
| 6187 |
if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) { |
| 6188 |
# |
| 6189 |
} else { |
| 6190 |
$self->{onerror}->(node => $child_el, |
| 6191 |
type => 'element not allowed:phrasing', |
| 6192 |
level => $self->{level}->{must}); |
| 6193 |
} |
| 6194 |
} elsif ($element_state->{phase} eq 'option') { |
| 6195 |
if ($child_nsuri eq $HTML_NS and $child_ln eq 'option') { |
| 6196 |
# |
| 6197 |
} else { |
| 6198 |
$self->{onerror}->(node => $child_el, |
| 6199 |
type => 'element not allowed', |
| 6200 |
level => $self->{level}->{must}); |
| 6201 |
} |
| 6202 |
} elsif ($element_state->{phase} eq 'any') { |
| 6203 |
if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) { |
| 6204 |
$element_state->{phase} = 'phrasing'; |
| 6205 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'option') { |
| 6206 |
$element_state->{phase} = 'option'; |
| 6207 |
} else { |
| 6208 |
$self->{onerror}->(node => $child_el, |
| 6209 |
type => 'element not allowed', |
| 6210 |
level => $self->{level}->{must}); |
| 6211 |
} |
| 6212 |
} else { |
| 6213 |
die "check_child_element: Bad |datalist| phase: $element_state->{phase}"; |
| 6214 |
} |
| 6215 |
}, |
| 6216 |
check_child_text => sub { |
| 6217 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 6218 |
if ($has_significant) { |
| 6219 |
if ($element_state->{phase} eq 'phrasing') { |
| 6220 |
# |
| 6221 |
} elsif ($element_state->{phase} eq 'any') { |
| 6222 |
$element_state->{phase} = 'phrasing'; |
| 6223 |
} else { |
| 6224 |
$self->{onerror}->(node => $child_node, |
| 6225 |
type => 'character not allowed', |
| 6226 |
level => $self->{level}->{must}); |
| 6227 |
} |
| 6228 |
} |
| 6229 |
}, |
| 6230 |
check_end => sub { |
| 6231 |
my ($self, $item, $element_state) = @_; |
| 6232 |
if ($element_state->{phase} eq 'phrasing') { |
| 6233 |
if ($element_state->{has_significant}) { |
| 6234 |
$item->{real_parent_state}->{has_significant} = 1; |
| 6235 |
} elsif ($item->{transparent}) { |
| 6236 |
# |
| 6237 |
} else { |
| 6238 |
$self->{onerror}->(node => $item->{node}, |
| 6239 |
type => 'no significant content', |
| 6240 |
level => $self->{level}->{should}); |
| 6241 |
} |
| 6242 |
} else { |
| 6243 |
## NOTE: Since the content model explicitly allows a |datalist| element |
| 6244 |
## being empty, we don't raise "no significant content" error for this |
| 6245 |
## element when there is no element. (We should raise an error for |
| 6246 |
## |<datalist><br></datalist>|, however.) |
| 6247 |
## NOTE: As a side-effect, when the |datalist| element only contains |
| 6248 |
## non-conforming content, then the |phase| flag has not changed from |
| 6249 |
## |any|, no "no significant content" error is raised neither. |
| 6250 |
$HTMLChecker{check_end}->(@_); |
| 6251 |
} |
| 6252 |
}, |
| 6253 |
}; |
| 6254 |
|
| 6255 |
$Element->{$HTML_NS}->{optgroup} = { |
| 6256 |
%HTMLChecker, |
| 6257 |
status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC, |
| 6258 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 6259 |
disabled => $GetHTMLBooleanAttrChecker->('disabled'), |
| 6260 |
label => sub {}, ## NOTE: Text [M12N] ## TODO: required |
| 6261 |
}, { |
| 6262 |
%HTMLAttrStatus, |
| 6263 |
%HTMLM12NCommonAttrStatus, |
| 6264 |
disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC, |
| 6265 |
label => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6266 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 6267 |
}), |
| 6268 |
check_child_element => sub { |
| 6269 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 6270 |
$child_is_transparent, $element_state) = @_; |
| 6271 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 6272 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 6273 |
$self->{onerror}->(node => $child_el, |
| 6274 |
type => 'element not allowed:minus', |
| 6275 |
level => $self->{level}->{must}); |
| 6276 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 6277 |
# |
| 6278 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'option') { |
| 6279 |
# |
| 6280 |
} else { |
| 6281 |
$self->{onerror}->(node => $child_el, type => 'element not allowed', |
| 6282 |
level => $self->{level}->{must}); |
| 6283 |
} |
| 6284 |
}, |
| 6285 |
check_child_text => sub { |
| 6286 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 6287 |
if ($has_significant) { |
| 6288 |
$self->{onerror}->(node => $child_node, type => 'character not allowed', |
| 6289 |
level => $self->{level}->{must}); |
| 6290 |
} |
| 6291 |
}, |
| 6292 |
}; |
| 6293 |
|
| 6294 |
$Element->{$HTML_NS}->{option} = { |
| 6295 |
%HTMLTextChecker, |
| 6296 |
status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC, |
| 6297 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 6298 |
disabled => $GetHTMLBooleanAttrChecker->('disabled'), |
| 6299 |
label => sub {}, ## NOTE: Text [M12N] |
| 6300 |
selected => $GetHTMLBooleanAttrChecker->('selected'), |
| 6301 |
value => sub {}, ## NOTE: CDATA [M12N] |
| 6302 |
}, { |
| 6303 |
%HTMLAttrStatus, |
| 6304 |
%HTMLM12NCommonAttrStatus, |
| 6305 |
disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC, |
| 6306 |
label => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6307 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 6308 |
sdaform => FEATURE_HTML20_RFC, |
| 6309 |
sdapref => FEATURE_HTML20_RFC, |
| 6310 |
selected => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6311 |
value => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6312 |
}), |
| 6313 |
}; |
| 6314 |
|
| 6315 |
$Element->{$HTML_NS}->{textarea} = { |
| 6316 |
%HTMLTextChecker, |
| 6317 |
status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC, |
| 6318 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 6319 |
accept => $HTMLIMTAttrChecker, ## TODO: MUST be a text-based type |
| 6320 |
accesskey => $HTMLAccesskeyAttrChecker, |
| 6321 |
autofocus => $GetHTMLBooleanAttrChecker->('autofocus'), |
| 6322 |
cols => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## TODO: SHOULD if wrap=hard [WF2] |
| 6323 |
disabled => $GetHTMLBooleanAttrChecker->('disabled'), |
| 6324 |
form => $HTMLFormAttrChecker, |
| 6325 |
## TODO: inputmode [WF2] |
| 6326 |
maxlength => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), |
| 6327 |
name => sub {}, ## NOTE: CDATA [M12N] |
| 6328 |
onformchange => $HTMLEventHandlerAttrChecker, |
| 6329 |
onforminput => $HTMLEventHandlerAttrChecker, |
| 6330 |
oninput => $HTMLEventHandlerAttrChecker, |
| 6331 |
## TODO: pattern [WF2] ## TODO: |title| special semantics |
| 6332 |
readonly => $GetHTMLBooleanAttrChecker->('readonly'), |
| 6333 |
required => $GetHTMLBooleanAttrChecker->('required'), |
| 6334 |
rows => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), |
| 6335 |
oninput => $HTMLEventHandlerAttrChecker, |
| 6336 |
oninvalid => $HTMLEventHandlerAttrChecker, |
| 6337 |
wrap => $GetHTMLEnumeratedAttrChecker->({soft => 1, hard => 1}), |
| 6338 |
}, { |
| 6339 |
%HTMLAttrStatus, |
| 6340 |
%HTMLM12NCommonAttrStatus, |
| 6341 |
accept => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 6342 |
'accept-charset' => FEATURE_HTML2X_RFC, |
| 6343 |
accesskey => FEATURE_M12N10_REC, |
| 6344 |
autofocus => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 6345 |
cols => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6346 |
datafld => FEATURE_HTML4_REC_RESERVED, |
| 6347 |
dataformatas => FEATURE_HTML4_REC_RESERVED, |
| 6348 |
datasrc => FEATURE_HTML4_REC_RESERVED, |
| 6349 |
disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC, |
| 6350 |
form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 6351 |
inputmode => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_XHTMLBASIC11_CR, |
| 6352 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 6353 |
maxlength => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 6354 |
name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6355 |
onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6356 |
onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6357 |
onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6358 |
onformchange => FEATURE_WF2_INFORMATIVE, |
| 6359 |
onforminput => FEATURE_WF2_INFORMATIVE, |
| 6360 |
oninput => FEATURE_WF2, |
| 6361 |
oninvalid => FEATURE_WF2, |
| 6362 |
onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6363 |
pattern => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 6364 |
readonly => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC, |
| 6365 |
required => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 6366 |
rows => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6367 |
sdaform => FEATURE_HTML20_RFC, |
| 6368 |
sdapref => FEATURE_HTML20_RFC, |
| 6369 |
tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6370 |
wrap => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 6371 |
}), |
| 6372 |
check_start => sub { |
| 6373 |
my ($self, $item, $element_state) = @_; |
| 6374 |
if ($self->{flag}->{has_label} and $self->{flag}->{has_labelable}) { |
| 6375 |
$self->{onerror}->(node => $item->{node}, |
| 6376 |
type => 'multiple labelable fae', |
| 6377 |
level => $self->{level}->{must}); |
| 6378 |
} else { |
| 6379 |
$self->{flag}->{has_labelable} = 2; |
| 6380 |
} |
| 6381 |
|
| 6382 |
$element_state->{uri_info}->{data}->{type}->{resource} = 1; |
| 6383 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 6384 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 6385 |
|
| 6386 |
$element_state->{id_type} = 'labelable'; |
| 6387 |
}, |
| 6388 |
}; |
| 6389 |
|
| 6390 |
$Element->{$HTML_NS}->{output} = { |
| 6391 |
%HTMLPhrasingContentChecker, |
| 6392 |
status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 6393 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 6394 |
## TODO: for [WF2] |
| 6395 |
form => $HTMLFormAttrChecker, |
| 6396 |
## TODO: name [WF2] |
| 6397 |
onformchange => $HTMLEventHandlerAttrChecker, |
| 6398 |
onforminput => $HTMLEventHandlerAttrChecker, |
| 6399 |
}, { |
| 6400 |
%HTMLAttrStatus, |
| 6401 |
for => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 6402 |
form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 6403 |
name => FEATURE_HTML5_DEFAULT | FEATURE_WF2X, |
| 6404 |
onchange => FEATURE_HTML5_DEFAULT | FEATURE_WF2, |
| 6405 |
onformchange => FEATURE_WF2, |
| 6406 |
onforminput => FEATURE_WF2, |
| 6407 |
}), |
| 6408 |
## NOTE: "The output element should be used when ..." [WF2] |
| 6409 |
}; |
| 6410 |
|
| 6411 |
$Element->{$HTML_NS}->{isindex} = { |
| 6412 |
%HTMLEmptyChecker, |
| 6413 |
status => FEATURE_M12N10_REC_DEPRECATED | |
| 6414 |
Whatpm::ContentChecker::FEATURE_DEPRECATED_SHOULD, ## [HTML4] |
| 6415 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 6416 |
prompt => sub {}, ## NOTE: Text [M12N] |
| 6417 |
}, { |
| 6418 |
%HTMLAttrStatus, |
| 6419 |
class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6420 |
dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6421 |
id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6422 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 6423 |
prompt => FEATURE_M12N10_REC_DEPRECATED, |
| 6424 |
sdapref => FEATURE_HTML20_RFC, |
| 6425 |
style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 6426 |
title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6427 |
}), |
| 6428 |
## TODO: Tests |
| 6429 |
## TODO: Tests for <nest/> in <isindex> |
| 6430 |
check_start => sub { |
| 6431 |
my ($self, $item, $element_state) = @_; |
| 6432 |
|
| 6433 |
$element_state->{uri_info}->{action}->{type}->{action} = 1; |
| 6434 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 6435 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 6436 |
}, |
| 6437 |
}; |
| 6438 |
|
| 6439 |
$Element->{$HTML_NS}->{script} = { |
| 6440 |
%HTMLChecker, |
| 6441 |
status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6442 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 6443 |
charset => sub { |
| 6444 |
my ($self, $attr) = @_; |
| 6445 |
|
| 6446 |
unless ($attr->owner_element->has_attribute_ns (undef, 'src')) { |
| 6447 |
$self->{onerror}->(type => 'attribute not allowed', |
| 6448 |
node => $attr, |
| 6449 |
level => $self->{level}->{must}); |
| 6450 |
} |
| 6451 |
|
| 6452 |
$HTMLCharsetChecker->($attr->value, @_); |
| 6453 |
}, |
| 6454 |
language => sub {}, ## NOTE: No syntax constraint according to HTML4. |
| 6455 |
src => $HTMLURIAttrChecker, ## TODO: pointed resource MUST be in type of type="" (resource error) |
| 6456 |
defer => $GetHTMLBooleanAttrChecker->('defer'), |
| 6457 |
async => $GetHTMLBooleanAttrChecker->('async'), |
| 6458 |
type => $HTMLIMTAttrChecker, ## TODO: MUST NOT: |charset=""| parameter |
| 6459 |
}, { |
| 6460 |
%HTMLAttrStatus, |
| 6461 |
async => FEATURE_HTML5_DEFAULT, |
| 6462 |
charset => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6463 |
defer => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6464 |
event => FEATURE_HTML4_REC_RESERVED, |
| 6465 |
for => FEATURE_HTML4_REC_RESERVED, |
| 6466 |
href => FEATURE_RDFA_PR, |
| 6467 |
id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 6468 |
language => FEATURE_M12N10_REC_DEPRECATED, |
| 6469 |
src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6470 |
type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6471 |
}), |
| 6472 |
check_start => sub { |
| 6473 |
my ($self, $item, $element_state) = @_; |
| 6474 |
|
| 6475 |
if ($item->{node}->has_attribute_ns (undef, 'src')) { |
| 6476 |
$element_state->{must_be_empty} = 1; |
| 6477 |
} else { |
| 6478 |
## NOTE: No content model conformance in HTML5 spec. |
| 6479 |
my $type = $item->{node}->get_attribute_ns (undef, 'type'); |
| 6480 |
my $language = $item->{node}->get_attribute_ns (undef, 'language'); |
| 6481 |
if ((defined $type and $type eq '') or |
| 6482 |
(defined $language and $language eq '')) { |
| 6483 |
$type = 'text/javascript'; |
| 6484 |
} elsif (defined $type) { |
| 6485 |
# |
| 6486 |
} elsif (defined $language) { |
| 6487 |
$type = 'text/' . $language; |
| 6488 |
} else { |
| 6489 |
$type = 'text/javascript'; |
| 6490 |
} |
| 6491 |
|
| 6492 |
if ($type =~ m[\A(?>(?>\x0D\x0A)?[\x09\x20])*([\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+)(?>(?>\x0D\x0A)?[\x09\x20])*/(?>(?>\x0D\x0A)?[\x09\x20])*([\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+)(?>(?>\x0D\x0A)?[\x09\x20])*(?>;|\z)]) { |
| 6493 |
$type = "$1/$2"; |
| 6494 |
$type =~ tr/A-Z/a-z/; ## NOTE: ASCII case-insensitive |
| 6495 |
## TODO: Though we strip prameter here, it should not be ignored for the purpose of conformance checking... |
| 6496 |
} |
| 6497 |
$element_state->{script_type} = $type; |
| 6498 |
} |
| 6499 |
|
| 6500 |
$element_state->{uri_info}->{src}->{type}->{resource} = 1; |
| 6501 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 6502 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 6503 |
|
| 6504 |
$element_state->{text} = ''; |
| 6505 |
}, |
| 6506 |
check_child_element => sub { |
| 6507 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 6508 |
$child_is_transparent, $element_state) = @_; |
| 6509 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 6510 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 6511 |
$self->{onerror}->(node => $child_el, |
| 6512 |
type => 'element not allowed:minus', |
| 6513 |
level => $self->{level}->{must}); |
| 6514 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 6515 |
# |
| 6516 |
} else { |
| 6517 |
if ($element_state->{must_be_empty}) { |
| 6518 |
$self->{onerror}->(node => $child_el, |
| 6519 |
type => 'element not allowed:empty', |
| 6520 |
level => $self->{level}->{must}); |
| 6521 |
} |
| 6522 |
} |
| 6523 |
}, |
| 6524 |
check_child_text => sub { |
| 6525 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 6526 |
if ($has_significant and |
| 6527 |
$element_state->{must_be_empty}) { |
| 6528 |
$self->{onerror}->(node => $child_node, |
| 6529 |
type => 'character not allowed:empty', |
| 6530 |
level => $self->{level}->{must}); |
| 6531 |
} |
| 6532 |
$element_state->{text} .= $child_node->data; |
| 6533 |
}, |
| 6534 |
check_end => sub { |
| 6535 |
my ($self, $item, $element_state) = @_; |
| 6536 |
unless ($element_state->{must_be_empty}) { |
| 6537 |
if ($element_state->{script_type} =~ m![+/][Xx][Mm][Ll]\z!) { |
| 6538 |
## NOTE: XML content should be checked by THIS instance of checker |
| 6539 |
## as part of normal tree validation. |
| 6540 |
$self->{onerror}->(node => $item->{node}, |
| 6541 |
type => 'XML script lang', |
| 6542 |
text => $element_state->{script_type}, |
| 6543 |
level => $self->{level}->{uncertain}); |
| 6544 |
## ISSUE: Should we raise some kind of error for |
| 6545 |
## <script type="text/xml">aaaaa</script>? |
| 6546 |
## NOTE: ^^^ This is why we throw an "uncertain" error. |
| 6547 |
} else { |
| 6548 |
$self->{onsubdoc}->({s => $element_state->{text}, |
| 6549 |
container_node => $item->{node}, |
| 6550 |
media_type => $element_state->{script_type}, |
| 6551 |
is_char_string => 1}); |
| 6552 |
} |
| 6553 |
|
| 6554 |
$HTMLChecker{check_end}->(@_); |
| 6555 |
} |
| 6556 |
}, |
| 6557 |
## TODO: There MUST be |type| unless the script type is JavaScript. (resource error) |
| 6558 |
## NOTE: "When used to include script data, the script data must be embedded |
| 6559 |
## inline, the format of the data must be given using the type attribute, |
| 6560 |
## and the src attribute must not be specified." - not testable. |
| 6561 |
## TODO: It would be possible to err <script type=text/plain src=...> |
| 6562 |
}; |
| 6563 |
## ISSUE: Significant check and text child node |
| 6564 |
|
| 6565 |
## NOTE: When script is disabled. |
| 6566 |
$Element->{$HTML_NS}->{noscript} = { |
| 6567 |
%HTMLTransparentChecker, |
| 6568 |
status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 6569 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 6570 |
%HTMLAttrStatus, |
| 6571 |
%HTMLM12NCommonAttrStatus, |
| 6572 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 6573 |
}), |
| 6574 |
check_start => sub { |
| 6575 |
my ($self, $item, $element_state) = @_; |
| 6576 |
|
| 6577 |
unless ($item->{node}->owner_document->manakai_is_html) { |
| 6578 |
$self->{onerror}->(node => $item->{node}, type => 'in XML:noscript', |
| 6579 |
level => $self->{level}->{must}); |
| 6580 |
} |
| 6581 |
|
| 6582 |
unless ($self->{flag}->{in_head}) { |
| 6583 |
$self->_add_minus_elements ($element_state, |
| 6584 |
{$HTML_NS => {noscript => 1}}); |
| 6585 |
} |
| 6586 |
|
| 6587 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 6588 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 6589 |
}, |
| 6590 |
check_child_element => sub { |
| 6591 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 6592 |
$child_is_transparent, $element_state) = @_; |
| 6593 |
if ($self->{flag}->{in_head}) { |
| 6594 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 6595 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 6596 |
$self->{onerror}->(node => $child_el, |
| 6597 |
type => 'element not allowed:minus', |
| 6598 |
level => $self->{level}->{must}); |
| 6599 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 6600 |
# |
| 6601 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'link') { |
| 6602 |
# |
| 6603 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') { |
| 6604 |
if ($child_el->has_attribute_ns (undef, 'scoped')) { |
| 6605 |
$self->{onerror}->(node => $child_el, |
| 6606 |
type => 'element not allowed:head noscript', |
| 6607 |
level => $self->{level}->{must}); |
| 6608 |
} |
| 6609 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'meta') { |
| 6610 |
my $http_equiv_attr |
| 6611 |
= $child_el->get_attribute_node_ns (undef, 'http-equiv'); |
| 6612 |
if ($http_equiv_attr) { |
| 6613 |
## TODO: case |
| 6614 |
if (lc $http_equiv_attr->value eq 'content-type') { |
| 6615 |
$self->{onerror}->(node => $child_el, |
| 6616 |
type => 'element not allowed:head noscript', |
| 6617 |
level => $self->{level}->{must}); |
| 6618 |
} else { |
| 6619 |
# |
| 6620 |
} |
| 6621 |
} else { |
| 6622 |
$self->{onerror}->(node => $child_el, |
| 6623 |
type => 'element not allowed:head noscript', |
| 6624 |
level => $self->{level}->{must}); |
| 6625 |
} |
| 6626 |
} else { |
| 6627 |
$self->{onerror}->(node => $child_el, |
| 6628 |
type => 'element not allowed:head noscript', |
| 6629 |
level => $self->{level}->{must}); |
| 6630 |
} |
| 6631 |
} else { |
| 6632 |
$HTMLTransparentChecker{check_child_element}->(@_); |
| 6633 |
} |
| 6634 |
}, |
| 6635 |
check_child_text => sub { |
| 6636 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 6637 |
if ($self->{flag}->{in_head}) { |
| 6638 |
if ($has_significant) { |
| 6639 |
$self->{onerror}->(node => $child_node, |
| 6640 |
type => 'character not allowed', |
| 6641 |
level => $self->{level}->{must}); |
| 6642 |
} |
| 6643 |
} else { |
| 6644 |
$HTMLTransparentChecker{check_child_text}->(@_); |
| 6645 |
} |
| 6646 |
}, |
| 6647 |
check_end => sub { |
| 6648 |
my ($self, $item, $element_state) = @_; |
| 6649 |
$self->_remove_minus_elements ($element_state); |
| 6650 |
if ($self->{flag}->{in_head}) { |
| 6651 |
$HTMLChecker{check_end}->(@_); |
| 6652 |
} else { |
| 6653 |
$HTMLPhrasingContentChecker{check_end}->(@_); |
| 6654 |
} |
| 6655 |
}, |
| 6656 |
}; |
| 6657 |
## ISSUE: Scripting is disabled: <head><noscript><html a></noscript></head> |
| 6658 |
|
| 6659 |
$Element->{$HTML_NS}->{'event-source'} = { |
| 6660 |
%HTMLEmptyChecker, |
| 6661 |
status => FEATURE_HTML5_LC_DROPPED, |
| 6662 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 6663 |
src => $HTMLURIAttrChecker, |
| 6664 |
}, { |
| 6665 |
%HTMLAttrStatus, |
| 6666 |
src => FEATURE_HTML5_LC_DROPPED, |
| 6667 |
}), |
| 6668 |
check_start => sub { |
| 6669 |
my ($self, $item, $element_state) = @_; |
| 6670 |
|
| 6671 |
$element_state->{uri_info}->{src}->{type}->{resource} = 1; |
| 6672 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 6673 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 6674 |
}, |
| 6675 |
}; |
| 6676 |
|
| 6677 |
$Element->{$HTML_NS}->{eventsource} = { |
| 6678 |
%HTMLEmptyChecker, |
| 6679 |
status => FEATURE_HTML5_DEFAULT, |
| 6680 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 6681 |
src => $HTMLURIAttrChecker, |
| 6682 |
}, { |
| 6683 |
%HTMLAttrStatus, |
| 6684 |
src => FEATURE_HTML5_DEFAULT, |
| 6685 |
}), |
| 6686 |
check_start => sub { |
| 6687 |
my ($self, $item, $element_state) = @_; |
| 6688 |
|
| 6689 |
$element_state->{uri_info}->{src}->{type}->{resource} = 1; |
| 6690 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 6691 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 6692 |
}, |
| 6693 |
}; |
| 6694 |
|
| 6695 |
$Element->{$HTML_NS}->{details} = { |
| 6696 |
%{$Element->{$HTML_NS}->{fieldset}}, |
| 6697 |
status => FEATURE_HTML5_WD, |
| 6698 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 6699 |
open => $GetHTMLBooleanAttrChecker->('open'), |
| 6700 |
}, { |
| 6701 |
%HTMLAttrStatus, |
| 6702 |
open => FEATURE_HTML5_WD, |
| 6703 |
}), |
| 6704 |
}; |
| 6705 |
|
| 6706 |
$Element->{$HTML_NS}->{datagrid} = { |
| 6707 |
%HTMLFlowContentChecker, |
| 6708 |
status => FEATURE_HTML5_WD, |
| 6709 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 6710 |
disabled => $GetHTMLBooleanAttrChecker->('disabled'), |
| 6711 |
multiple => $GetHTMLBooleanAttrChecker->('multiple'), |
| 6712 |
}, { |
| 6713 |
%HTMLAttrStatus, |
| 6714 |
disabled => FEATURE_HTML5_WD, |
| 6715 |
multiple => FEATURE_HTML5_WD, |
| 6716 |
}), |
| 6717 |
check_start => sub { |
| 6718 |
my ($self, $item, $element_state) = @_; |
| 6719 |
|
| 6720 |
$self->_add_minus_elements ($element_state, |
| 6721 |
{$HTML_NS => {a => 1, datagrid => 1}}); |
| 6722 |
$element_state->{phase} = 'any'; |
| 6723 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 6724 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 6725 |
}, |
| 6726 |
## NOTE: Flow -(text* (table|select|datalist) Flow*) | table | select | |
| 6727 |
## datalist | Empty |
| 6728 |
check_child_element => sub { |
| 6729 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 6730 |
$child_is_transparent, $element_state) = @_; |
| 6731 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 6732 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 6733 |
$self->{onerror}->(node => $child_el, |
| 6734 |
type => 'element not allowed:minus', |
| 6735 |
level => $self->{level}->{must}); |
| 6736 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 6737 |
# |
| 6738 |
} elsif ($element_state->{phase} eq 'flow') { |
| 6739 |
if ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) { |
| 6740 |
if (not $element_state->{has_element} and |
| 6741 |
$child_nsuri eq $HTML_NS and |
| 6742 |
{ |
| 6743 |
table => 1, select => 1, datalist => 1, |
| 6744 |
}->{$child_ln}) { |
| 6745 |
$self->{onerror}->(node => $child_el, |
| 6746 |
type => 'element not allowed', |
| 6747 |
level => $self->{level}->{must}); |
| 6748 |
} else { |
| 6749 |
# |
| 6750 |
} |
| 6751 |
} else { |
| 6752 |
$self->{onerror}->(node => $child_el, |
| 6753 |
type => 'element not allowed', ## TODO: :flow |
| 6754 |
level => $self->{level}->{must}); |
| 6755 |
} |
| 6756 |
$element_state->{has_element} = 1; |
| 6757 |
} elsif ($element_state->{phase} eq 'any') { |
| 6758 |
if ($child_nsuri eq $HTML_NS and |
| 6759 |
{table => 1, select => 1, datalist => 1}->{$child_ln}) { |
| 6760 |
$element_state->{phase} = 'none'; |
| 6761 |
} elsif ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) { |
| 6762 |
$element_state->{has_element} = 1; |
| 6763 |
$element_state->{phase} = 'flow'; |
| 6764 |
} else { |
| 6765 |
$self->{onerror}->(node => $child_el, |
| 6766 |
type => 'element not allowed', |
| 6767 |
level => $self->{level}->{must}); |
| 6768 |
} |
| 6769 |
} elsif ($element_state->{phase} eq 'none') { |
| 6770 |
$self->{onerror}->(node => $child_el, |
| 6771 |
type => 'element not allowed', |
| 6772 |
level => $self->{level}->{must}); |
| 6773 |
} else { |
| 6774 |
die "check_child_element: Bad |datagrid| phase: $element_state->{phase}"; |
| 6775 |
} |
| 6776 |
}, |
| 6777 |
check_child_text => sub { |
| 6778 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 6779 |
if ($has_significant) { |
| 6780 |
if ($element_state->{phase} eq 'flow') { |
| 6781 |
# |
| 6782 |
} elsif ($element_state->{phase} eq 'any') { |
| 6783 |
$element_state->{phase} = 'flow'; |
| 6784 |
} else { |
| 6785 |
$self->{onerror}->(node => $child_node, |
| 6786 |
type => 'character not allowed', |
| 6787 |
level => $self->{level}->{must}); |
| 6788 |
} |
| 6789 |
} |
| 6790 |
}, |
| 6791 |
check_end => sub { |
| 6792 |
my ($self, $item, $element_state) = @_; |
| 6793 |
$self->_remove_minus_elements ($element_state); |
| 6794 |
|
| 6795 |
if ($element_state->{phase} eq 'flow') { |
| 6796 |
if ($element_state->{has_significant}) { |
| 6797 |
$item->{real_parent_state}->{has_significant} = 1; |
| 6798 |
} elsif ($item->{transparent}) { |
| 6799 |
# |
| 6800 |
} else { |
| 6801 |
$self->{onerror}->(node => $item->{node}, |
| 6802 |
type => 'no significant content', |
| 6803 |
level => $self->{level}->{should}); |
| 6804 |
} |
| 6805 |
} else { |
| 6806 |
## NOTE: Since the content model explicitly allows a |datagird| element |
| 6807 |
## being empty, we don't raise "no significant content" error for this |
| 6808 |
## element when there is no element. (We should raise an error for |
| 6809 |
## |<datagrid><br></datagrid>|, however.) |
| 6810 |
## NOTE: As a side-effect, when the |datagrid| element only contains |
| 6811 |
## non-conforming content, then the |phase| flag has not changed from |
| 6812 |
## |any|, no "no significant content" error is raised neither. |
| 6813 |
## NOTE: Another side-effect of the current implementation: |
| 6814 |
## |<daragrid><datagrid/></datagrid>| has no "no significant content" |
| 6815 |
## error at all. |
| 6816 |
$HTMLChecker{check_end}->(@_); |
| 6817 |
} |
| 6818 |
}, |
| 6819 |
}; |
| 6820 |
|
| 6821 |
$Element->{$HTML_NS}->{command} = { |
| 6822 |
%HTMLEmptyChecker, |
| 6823 |
status => FEATURE_HTML5_WD, |
| 6824 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 6825 |
checked => $GetHTMLBooleanAttrChecker->('checked'), |
| 6826 |
default => $GetHTMLBooleanAttrChecker->('default'), |
| 6827 |
disabled => $GetHTMLBooleanAttrChecker->('disabled'), |
| 6828 |
icon => $HTMLURIAttrChecker, |
| 6829 |
label => sub { }, ## NOTE: No conformance creteria |
| 6830 |
radiogroup => sub { }, ## NOTE: No conformance creteria |
| 6831 |
type => sub { |
| 6832 |
my ($self, $attr) = @_; |
| 6833 |
my $value = $attr->value; |
| 6834 |
unless ({command => 1, checkbox => 1, radio => 1}->{$value}) { |
| 6835 |
$self->{onerror}->(node => $attr, type => 'invalid attribute value', |
| 6836 |
level => $self->{level}->{must}); |
| 6837 |
} |
| 6838 |
}, |
| 6839 |
}, { |
| 6840 |
%HTMLAttrStatus, |
| 6841 |
checked => FEATURE_HTML5_WD, |
| 6842 |
default => FEATURE_HTML5_WD, |
| 6843 |
disabled => FEATURE_HTML5_WD, |
| 6844 |
icon => FEATURE_HTML5_WD, |
| 6845 |
label => FEATURE_HTML5_WD, |
| 6846 |
radiogroup => FEATURE_HTML5_WD, |
| 6847 |
type => FEATURE_HTML5_WD, |
| 6848 |
}), |
| 6849 |
check_start => sub { |
| 6850 |
my ($self, $item, $element_state) = @_; |
| 6851 |
|
| 6852 |
$element_state->{uri_info}->{icon}->{type}->{embedded} = 1; |
| 6853 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 6854 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 6855 |
}, |
| 6856 |
}; |
| 6857 |
|
| 6858 |
$Element->{$HTML_NS}->{bb} = { |
| 6859 |
%HTMLPhrasingContentChecker, |
| 6860 |
status => FEATURE_HTML5_DEFAULT, |
| 6861 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 6862 |
type => $GetHTMLEnumeratedAttrChecker->({makeapp => 1}), |
| 6863 |
}, { |
| 6864 |
%HTMLAttrStatus, |
| 6865 |
type => FEATURE_HTML5_DEFAULT, |
| 6866 |
}), |
| 6867 |
check_start => sub { |
| 6868 |
my ($self, $item, $element_state) = @_; |
| 6869 |
$self->_add_minus_elements ($element_state, $HTMLInteractiveContent); |
| 6870 |
|
| 6871 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 6872 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 6873 |
}, |
| 6874 |
check_end => sub { |
| 6875 |
my ($self, $item, $element_state) = @_; |
| 6876 |
$self->_remove_minus_elements ($element_state); |
| 6877 |
|
| 6878 |
$HTMLTransparentChecker{check_end}->(@_); |
| 6879 |
}, |
| 6880 |
}; |
| 6881 |
|
| 6882 |
$Element->{$HTML_NS}->{menu} = { |
| 6883 |
%HTMLPhrasingContentChecker, |
| 6884 |
#status => FEATURE_M12N10_REC_DEPRECATED | FEATURE_HTML5_WD, |
| 6885 |
status => FEATURE_M12N10_REC | FEATURE_HTML5_WD, |
| 6886 |
## NOTE: We don't want any |menu| element warned as deprecated. |
| 6887 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 6888 |
autosubmit => $GetHTMLBooleanAttrChecker->('autosubmit'), |
| 6889 |
compact => $GetHTMLBooleanAttrChecker->('compact'), |
| 6890 |
## ISSUE: <menu id=""><p contextmenu=""> match? (In the current |
| 6891 |
## implementation, it does not match.) |
| 6892 |
label => sub { }, ## NOTE: No conformance creteria |
| 6893 |
type => $GetHTMLEnumeratedAttrChecker->({context => 1, toolbar => 1}), |
| 6894 |
}, { |
| 6895 |
%HTMLAttrStatus, |
| 6896 |
%HTMLM12NCommonAttrStatus, |
| 6897 |
align => FEATURE_HTML2X_RFC, |
| 6898 |
autosubmit => FEATURE_HTML5_DROPPED, |
| 6899 |
compat => FEATURE_M12N10_REC_DEPRECATED, |
| 6900 |
label => FEATURE_HTML5_WD, |
| 6901 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 6902 |
sdaform => FEATURE_HTML20_RFC, |
| 6903 |
sdapref => FEATURE_HTML20_RFC, |
| 6904 |
type => FEATURE_HTML5_WD, |
| 6905 |
}), |
| 6906 |
check_start => sub { |
| 6907 |
my ($self, $item, $element_state) = @_; |
| 6908 |
$element_state->{phase} = 'li or phrasing'; |
| 6909 |
$element_state->{in_menu_original} = $self->{flag}->{in_menu}; |
| 6910 |
$self->{flag}->{in_menu} = 1; |
| 6911 |
|
| 6912 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 6913 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 6914 |
$element_state->{id_type} = 'menu'; |
| 6915 |
}, |
| 6916 |
check_child_element => sub { |
| 6917 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 6918 |
$child_is_transparent, $element_state) = @_; |
| 6919 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 6920 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 6921 |
$self->{onerror}->(node => $child_el, |
| 6922 |
type => 'element not allowed:minus', |
| 6923 |
level => $self->{level}->{must}); |
| 6924 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 6925 |
# |
| 6926 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'li') { |
| 6927 |
if ($element_state->{phase} eq 'li') { |
| 6928 |
# |
| 6929 |
} elsif ($element_state->{phase} eq 'li or phrasing') { |
| 6930 |
$element_state->{phase} = 'li'; |
| 6931 |
} else { |
| 6932 |
$self->{onerror}->(node => $child_el, type => 'element not allowed', |
| 6933 |
level => $self->{level}->{must}); |
| 6934 |
} |
| 6935 |
} elsif ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) { |
| 6936 |
if ($element_state->{phase} eq 'phrasing') { |
| 6937 |
# |
| 6938 |
} elsif ($element_state->{phase} eq 'li or phrasing') { |
| 6939 |
$element_state->{phase} = 'phrasing'; |
| 6940 |
} else { |
| 6941 |
$self->{onerror}->(node => $child_el, type => 'element not allowed', |
| 6942 |
level => $self->{level}->{must}); |
| 6943 |
} |
| 6944 |
} else { |
| 6945 |
$self->{onerror}->(node => $child_el, type => 'element not allowed', |
| 6946 |
level => $self->{level}->{must}); |
| 6947 |
} |
| 6948 |
}, |
| 6949 |
check_child_text => sub { |
| 6950 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 6951 |
if ($has_significant) { |
| 6952 |
if ($element_state->{phase} eq 'phrasing') { |
| 6953 |
# |
| 6954 |
} elsif ($element_state->{phase} eq 'li or phrasing') { |
| 6955 |
$element_state->{phase} = 'phrasing'; |
| 6956 |
} else { |
| 6957 |
$self->{onerror}->(node => $child_node, |
| 6958 |
type => 'character not allowed', |
| 6959 |
level => $self->{level}->{must}); |
| 6960 |
} |
| 6961 |
} |
| 6962 |
}, |
| 6963 |
check_end => sub { |
| 6964 |
my ($self, $item, $element_state) = @_; |
| 6965 |
delete $self->{flag}->{in_menu} unless $element_state->{in_menu_original}; |
| 6966 |
|
| 6967 |
if ($element_state->{phase} eq 'li') { |
| 6968 |
$HTMLChecker{check_end}->(@_); |
| 6969 |
} else { # 'phrasing' or 'li or phrasing' |
| 6970 |
$HTMLPhrasingContentChecker{check_end}->(@_); |
| 6971 |
} |
| 6972 |
}, |
| 6973 |
}; |
| 6974 |
|
| 6975 |
$Element->{$HTML_NS}->{datatemplate} = { |
| 6976 |
%HTMLChecker, |
| 6977 |
status => FEATURE_HTML5_AT_RISK, |
| 6978 |
check_child_element => sub { |
| 6979 |
my ($self, $item, $child_el, $child_nsuri, $child_ln, |
| 6980 |
$child_is_transparent, $element_state) = @_; |
| 6981 |
if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and |
| 6982 |
$IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) { |
| 6983 |
$self->{onerror}->(node => $child_el, |
| 6984 |
type => 'element not allowed:minus', |
| 6985 |
level => $self->{level}->{must}); |
| 6986 |
} elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) { |
| 6987 |
# |
| 6988 |
} elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'rule') { |
| 6989 |
# |
| 6990 |
} else { |
| 6991 |
$self->{onerror}->(node => $child_el, |
| 6992 |
type => 'element not allowed:datatemplate', |
| 6993 |
level => $self->{level}->{must}); |
| 6994 |
} |
| 6995 |
}, |
| 6996 |
check_child_text => sub { |
| 6997 |
my ($self, $item, $child_node, $has_significant, $element_state) = @_; |
| 6998 |
if ($has_significant) { |
| 6999 |
$self->{onerror}->(node => $child_node, type => 'character not allowed', |
| 7000 |
level => $self->{level}->{must}); |
| 7001 |
} |
| 7002 |
}, |
| 7003 |
is_xml_root => 1, |
| 7004 |
}; |
| 7005 |
|
| 7006 |
$Element->{$HTML_NS}->{rule} = { |
| 7007 |
%HTMLChecker, |
| 7008 |
status => FEATURE_HTML5_AT_RISK, |
| 7009 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 7010 |
condition => $HTMLSelectorsAttrChecker, |
| 7011 |
mode => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(), |
| 7012 |
}, { |
| 7013 |
%HTMLAttrStatus, |
| 7014 |
condition => FEATURE_HTML5_AT_RISK, |
| 7015 |
mode => FEATURE_HTML5_AT_RISK, |
| 7016 |
}), |
| 7017 |
check_start => sub { |
| 7018 |
my ($self, $item, $element_state) = @_; |
| 7019 |
|
| 7020 |
$self->_add_plus_elements ($element_state, {$HTML_NS => {nest => 1}}); |
| 7021 |
$element_state->{in_rule_original} = $self->{flag}->{in_rule}; |
| 7022 |
$self->{flag}->{in_rule} = 1; |
| 7023 |
|
| 7024 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 7025 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 7026 |
}, |
| 7027 |
check_child_element => sub { }, |
| 7028 |
check_child_text => sub { }, |
| 7029 |
check_end => sub { |
| 7030 |
my ($self, $item, $element_state) = @_; |
| 7031 |
|
| 7032 |
$self->_remove_plus_elements ($element_state); |
| 7033 |
delete $self->{flag}->{in_rule} unless $element_state->{in_rule_original}; |
| 7034 |
|
| 7035 |
$HTMLChecker{check_end}->(@_); |
| 7036 |
}, |
| 7037 |
## NOTE: "MAY be anything that, when the parent |datatemplate| |
| 7038 |
## is applied to some conforming data, results in a conforming DOM tree.": |
| 7039 |
## We don't check against this. |
| 7040 |
}; |
| 7041 |
|
| 7042 |
$Element->{$HTML_NS}->{nest} = { |
| 7043 |
%HTMLEmptyChecker, |
| 7044 |
status => FEATURE_HTML5_AT_RISK, |
| 7045 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 7046 |
filter => $HTMLSelectorsAttrChecker, |
| 7047 |
mode => sub { |
| 7048 |
my ($self, $attr) = @_; |
| 7049 |
my $value = $attr->value; |
| 7050 |
if ($value !~ /\A[^\x09\x0A\x0C\x0D\x20]+\z/) { |
| 7051 |
$self->{onerror}->(node => $attr, type => 'mode:syntax error', |
| 7052 |
level => $self->{level}->{must}); |
| 7053 |
} |
| 7054 |
}, |
| 7055 |
}, { |
| 7056 |
%HTMLAttrStatus, |
| 7057 |
filter => FEATURE_HTML5_AT_RISK, |
| 7058 |
mode => FEATURE_HTML5_AT_RISK, |
| 7059 |
}), |
| 7060 |
}; |
| 7061 |
|
| 7062 |
$Element->{$HTML_NS}->{legend} = { |
| 7063 |
%HTMLPhrasingContentChecker, |
| 7064 |
status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 7065 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 7066 |
accesskey => $HTMLAccesskeyAttrChecker, |
| 7067 |
# align => $GetHTMLEnumeratedAttrChecker->({ |
| 7068 |
# top => 1, bottom => 1, left => 1, right => 1, |
| 7069 |
# }), |
| 7070 |
}, { |
| 7071 |
%HTMLAttrStatus, |
| 7072 |
%HTMLM12NCommonAttrStatus, |
| 7073 |
accesskey => FEATURE_M12N10_REC, |
| 7074 |
align => FEATURE_M12N10_REC_DEPRECATED, |
| 7075 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 7076 |
}), |
| 7077 |
}; |
| 7078 |
|
| 7079 |
$Element->{$HTML_NS}->{div} = { |
| 7080 |
%HTMLFlowContentChecker, |
| 7081 |
status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC, |
| 7082 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 7083 |
align => $GetHTMLEnumeratedAttrChecker->({ |
| 7084 |
left => 1, center => 1, right => 1, justify => 1, |
| 7085 |
}), |
| 7086 |
}, { |
| 7087 |
%HTMLAttrStatus, |
| 7088 |
%HTMLM12NXHTML2CommonAttrStatus, |
| 7089 |
align => FEATURE_M12N10_REC_DEPRECATED, |
| 7090 |
datafld => FEATURE_HTML4_REC_RESERVED, |
| 7091 |
dataformatas => FEATURE_HTML4_REC_RESERVED, |
| 7092 |
datasrc => FEATURE_HTML4_REC_RESERVED, |
| 7093 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 7094 |
}), |
| 7095 |
check_start => sub { |
| 7096 |
my ($self, $item, $element_state) = @_; |
| 7097 |
|
| 7098 |
$element_state->{uri_info}->{datasrc}->{type}->{resource} = 1; |
| 7099 |
$element_state->{uri_info}->{template}->{type}->{resource} = 1; |
| 7100 |
$element_state->{uri_info}->{ref}->{type}->{resource} = 1; |
| 7101 |
}, |
| 7102 |
}; |
| 7103 |
|
| 7104 |
$Element->{$HTML_NS}->{center} = { |
| 7105 |
%HTMLFlowContentChecker, |
| 7106 |
status => FEATURE_M12N10_REC_DEPRECATED, |
| 7107 |
check_attrs => $GetHTMLAttrsChecker->({}, { |
| 7108 |
%HTMLAttrStatus, |
| 7109 |
%HTMLM12NCommonAttrStatus, |
| 7110 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 7111 |
}), |
| 7112 |
}; |
| 7113 |
|
| 7114 |
$Element->{$HTML_NS}->{font} = { |
| 7115 |
%HTMLTransparentChecker, |
| 7116 |
status => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC_DEPRECATED, |
| 7117 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 7118 |
## TODO: HTML4 |size|, |color|, |face| |
| 7119 |
}, { |
| 7120 |
%HTMLAttrStatus, |
| 7121 |
class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 7122 |
color => FEATURE_M12N10_REC_DEPRECATED, |
| 7123 |
dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 7124 |
face => FEATURE_M12N10_REC_DEPRECATED, |
| 7125 |
id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 7126 |
lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 7127 |
size => FEATURE_M12N10_REC_DEPRECATED, |
| 7128 |
style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC, |
| 7129 |
title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 7130 |
}), |
| 7131 |
## NOTE: When the |font| element was defined in the HTML5 specification, |
| 7132 |
## it is allowed only in a document with the WYSIWYG signature. The |
| 7133 |
## checker does not check whether there is the signature, since the |
| 7134 |
## signature is dropped, too, and has never been implemented. (In addition, |
| 7135 |
## for any |font| element an "element not defined" error is raised anyway, |
| 7136 |
## such that we don't have to raise an additional error.) |
| 7137 |
}; |
| 7138 |
|
| 7139 |
$Element->{$HTML_NS}->{basefont} = { |
| 7140 |
%HTMLEmptyChecker, |
| 7141 |
status => FEATURE_M12N10_REC_DEPRECATED, |
| 7142 |
check_attrs => $GetHTMLAttrsChecker->({ |
| 7143 |
## TODO: color, face, size |
| 7144 |
}, { |
| 7145 |
%HTMLAttrStatus, |
| 7146 |
color => FEATURE_M12N10_REC_DEPRECATED, |
| 7147 |
face => FEATURE_M12N10_REC_DEPRECATED, |
| 7148 |
#id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED, |
| 7149 |
id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC, |
| 7150 |
size => FEATURE_M12N10_REC_DEPRECATED, |
| 7151 |
}), |
| 7152 |
}; |
| 7153 |
|
| 7154 |
## TODO: frameset FEATURE_M12N10_REC |
| 7155 |
## class title id cols rows onload onunload style(x10) |
| 7156 |
## frame frameborder longdesc marginheight marginwidth noresize scrolling src name(deprecated) class,id,title,style(x10) |
| 7157 |
## noframes Common, lang(xhtml10) |
| 7158 |
|
| 7159 |
## TODO: CR: rbc rtc @rbspan (M12NXHTML2Common) |
| 7160 |
|
| 7161 |
## TODO: xmp, listing, plaintext FEATURE_HTML32_REC_OBSOLETE |
| 7162 |
## TODO: ^^^ lang, dir, id, class [HTML 2.x] sdaform [HTML 2.0] |
| 7163 |
## xmp, listing sdapref[HTML2,0] |
| 7164 |
|
| 7165 |
=pod |
| 7166 |
|
| 7167 |
WF2: Documents MUST comply to [CHARMOD]. |
| 7168 |
WF2: Vencor extensions MUST NOT be used. |
| 7169 |
|
| 7170 |
HTML 2.0 nextid @n |
| 7171 |
|
| 7172 |
RFC 2659: CERTS CRYPTOPTS |
| 7173 |
|
| 7174 |
ISO-HTML: pre-html, divN |
| 7175 |
|
| 7176 |
XHTML2: blockcode (Common), h (Common), separator (Common), l (Common), |
| 7177 |
di (Common), nl (Common), handler (Common, type), standby (Common), |
| 7178 |
summary (Common) |
| 7179 |
|
| 7180 |
Access & XHTML2: access (LC) |
| 7181 |
|
| 7182 |
XML Events & XForms (for XHTML2 support; very, very low priority) |
| 7183 |
|
| 7184 |
=cut |
| 7185 |
|
| 7186 |
## NOTE: Where RFC 2659 allows additional attributes is unclear. |
| 7187 |
## We added them only to |a|. |link| and |form| might also allow them |
| 7188 |
## in theory. |
| 7189 |
|
| 7190 |
$Whatpm::ContentChecker::Namespace->{$HTML_NS}->{loaded} = 1; |
| 7191 |
|
| 7192 |
1; |