Parent Directory
|
Revision Log
++ whatpm/t/ChangeLog 10 May 2008 05:54:08 -0000 2008-05-10 Wakaba <wakaba@suika.fam.cx> * content-model-2.dat: Boolean attributes now allow uppercase letters (HTML5 revision 1572). ++ whatpm/Whatpm/ChangeLog 10 May 2008 05:57:19 -0000 2008-05-10 Wakaba <wakaba@suika.fam.cx> * CacheManifest.pm: Don't replace U+0000 NULL (HTML5 revision 1553). ++ whatpm/Whatpm/ContentChecker/ChangeLog 10 May 2008 05:54:35 -0000 2008-05-10 Wakaba <wakaba@suika.fam.cx> * HTML.pm ($GetHTMLBooleanAttrChecker): Allow uppercase letters (HTML5 revision 1572).
| 1 | wakaba | 1.1 | package Whatpm::ContentChecker; |
| 2 | use strict; | ||
| 3 | require Whatpm::ContentChecker; | ||
| 4 | |||
| 5 | my $HTML_NS = q<http://www.w3.org/1999/xhtml>; | ||
| 6 | |||
| 7 | wakaba | 1.58 | sub FEATURE_HTML5_ROLE () { |
| 8 | Whatpm::ContentChecker::FEATURE_STATUS_WD | ||
| 9 | ## TODO: svg:*/@role | ||
| 10 | } | ||
| 11 | |||
| 12 | wakaba | 1.54 | sub FEATURE_HTML5_LC () { |
| 13 | Whatpm::ContentChecker::FEATURE_STATUS_LC | | ||
| 14 | Whatpm::ContentChecker::FEATURE_ALLOWED | ||
| 15 | } | ||
| 16 | sub FEATURE_HTML5_AT_RISK () { | ||
| 17 | Whatpm::ContentChecker::FEATURE_STATUS_WD | | ||
| 18 | Whatpm::ContentChecker::FEATURE_ALLOWED | ||
| 19 | } | ||
| 20 | sub FEATURE_HTML5_WD () { | ||
| 21 | Whatpm::ContentChecker::FEATURE_STATUS_WD | | ||
| 22 | Whatpm::ContentChecker::FEATURE_ALLOWED | ||
| 23 | } | ||
| 24 | sub FEATURE_HTML5_FD () { | ||
| 25 | Whatpm::ContentChecker::FEATURE_STATUS_WD | | ||
| 26 | Whatpm::ContentChecker::FEATURE_ALLOWED | ||
| 27 | } | ||
| 28 | sub FEATURE_HTML5_DEFAULT () { | ||
| 29 | Whatpm::ContentChecker::FEATURE_STATUS_WD | | ||
| 30 | Whatpm::ContentChecker::FEATURE_ALLOWED | ||
| 31 | wakaba | 1.49 | } |
| 32 | wakaba | 1.54 | sub FEATURE_HTML5_DROPPED () { |
| 33 | ## NOTE: Was part of HTML5, but was dropped. | ||
| 34 | wakaba | 1.49 | Whatpm::ContentChecker::FEATURE_STATUS_WD |
| 35 | } | ||
| 36 | wakaba | 1.54 | sub FEATURE_WF2 () { |
| 37 | Whatpm::ContentChecker::FEATURE_STATUS_LC | | ||
| 38 | Whatpm::ContentChecker::FEATURE_ALLOWED | ||
| 39 | } | ||
| 40 | wakaba | 1.56 | sub FEATURE_WF2_DEPRECATED () { |
| 41 | Whatpm::ContentChecker::FEATURE_STATUS_LC | ||
| 42 | ## NOTE: MUST NOT be used. | ||
| 43 | } | ||
| 44 | wakaba | 1.49 | |
| 45 | wakaba | 1.61 | ## NOTE: Metainformation Attributes Module by W3C XHTML2 WG. |
| 46 | sub FEATURE_RDFA_LC () { | ||
| 47 | Whatpm::ContentChecker::FEATURE_STATUS_LC | ||
| 48 | } | ||
| 49 | wakaba | 1.58 | |
| 50 | wakaba | 1.82 | sub FEATURE_RDFA_ED () { |
| 51 | Whatpm::ContentChecker::FEATURE_STATUS_WD | ||
| 52 | } | ||
| 53 | |||
| 54 | wakaba | 1.58 | ## NOTE: XHTML Role LCWD has almost no information on how the |role| |
| 55 | ## attribute can be used- the only requirements for that matter is: | ||
| 56 | ## "the attribute MUST be referenced using its namespace-qualified form" (and | ||
| 57 | ## this is a host language conformance!). | ||
| 58 | wakaba | 1.82 | sub FEATURE_ROLE_LC () { |
| 59 | Whatpm::ContentChecker::FEATURE_STATUS_LC | ||
| 60 | } | ||
| 61 | |||
| 62 | sub FEATURE_XHTML2_ED () { | ||
| 63 | Whatpm::ContentChecker::FEATURE_STATUS_WD | ||
| 64 | } | ||
| 65 | wakaba | 1.58 | |
| 66 | wakaba | 1.55 | sub FEATURE_XHTMLBASIC11_CR () { |
| 67 | ## NOTE: Only additions to M12N10_REC are marked. | ||
| 68 | Whatpm::ContentChecker::FEATURE_STATUS_CR | ||
| 69 | } | ||
| 70 | sub FEATURE_XHTMLBASIC11_CR_DEPRECATED () { | ||
| 71 | Whatpm::ContentChecker::FEATURE_STATUS_CR | | ||
| 72 | Whatpm::ContentChecker::FEATURE_DEPRECATED_INFO | ||
| 73 | } | ||
| 74 | |||
| 75 | wakaba | 1.82 | sub FEATURE_M12N11_LC () { |
| 76 | Whatpm::ContentChecker::FEATURE_STATUS_LC | ||
| 77 | } | ||
| 78 | |||
| 79 | wakaba | 1.49 | ## NOTE: M12N10 status is based on its abstract module definition. |
| 80 | ## It contains a number of problems. (However, again, it's a REC!) | ||
| 81 | wakaba | 1.54 | sub FEATURE_M12N10_REC () { |
| 82 | ## NOTE: Oh, XHTML m12n 1.0 passed the CR phase! W3C Process suck! | ||
| 83 | Whatpm::ContentChecker::FEATURE_STATUS_REC | ||
| 84 | } | ||
| 85 | sub FEATURE_M12N10_REC_DEPRECATED () { | ||
| 86 | Whatpm::ContentChecker::FEATURE_STATUS_REC | | ||
| 87 | Whatpm::ContentChecker::FEATURE_DEPRECATED_INFO | ||
| 88 | } | ||
| 89 | wakaba | 1.58 | ## NOTE: XHTML M12N 1.1 is a LC at the time of writing and no |
| 90 | ## addition from 1.0. | ||
| 91 | wakaba | 1.49 | |
| 92 | ## NOTE: XHTML10 status is based on its transitional and frameset DTDs | ||
| 93 | ## (second edition). Only missing attributes from M12N10 abstract | ||
| 94 | ## definition are added. | ||
| 95 | wakaba | 1.54 | sub FEATURE_XHTML10_REC () { |
| 96 | Whatpm::ContentChecker::FEATURE_STATUS_CR | ||
| 97 | } | ||
| 98 | |||
| 99 | wakaba | 1.61 | ## NOTE: Diff from HTML4. |
| 100 | sub FEATURE_ISOHTML_PREPARATION () { ## Informative documentation | ||
| 101 | Whatpm::ContentChecker::FEATURE_STATUS_CR | ||
| 102 | } | ||
| 103 | wakaba | 1.58 | |
| 104 | wakaba | 1.49 | ## NOTE: HTML4 status is based on its transitional and frameset DTDs (HTML |
| 105 | ## 4.01). Only missing attributes from XHTML10 are added. | ||
| 106 | wakaba | 1.54 | sub FEATURE_HTML4_REC_RESERVED () { |
| 107 | Whatpm::ContentChecker::FEATURE_STATUS_WD | ||
| 108 | } | ||
| 109 | |||
| 110 | ## TODO: According to HTML4 definition, authors SHOULD use style sheets | ||
| 111 | ## rather than presentational attributes (deprecated or not deprecated). | ||
| 112 | wakaba | 1.48 | |
| 113 | wakaba | 1.61 | ## NOTE: Diff from HTML4. |
| 114 | sub FEATURE_HTML32_REC_OBSOLETE () { | ||
| 115 | Whatpm::ContentChecker::FEATURE_STATUS_CR | | ||
| 116 | Whatpm::ContentChecker::FEATURE_DEPRECATED_SHOULD | ||
| 117 | ## NOTE: Lowercase normative "should". | ||
| 118 | } | ||
| 119 | |||
| 120 | sub FEATURE_RFC2659 () { ## Experimental RFC | ||
| 121 | Whatpm::ContentChecker::FEATURE_STATUS_CR | ||
| 122 | } | ||
| 123 | |||
| 124 | ## NOTE: HTML 2.x - diff from HTML 2.0 and not in newer versions. | ||
| 125 | sub FEATURE_HTML2X_RFC () { ## Proposed Standard, obsolete | ||
| 126 | Whatpm::ContentChecker::FEATURE_STATUS_CR | ||
| 127 | } | ||
| 128 | |||
| 129 | ## NOTE: Diff from HTML 2.0. | ||
| 130 | sub FEATURE_RFC1942 () { ## Experimental RFC, obsolete | ||
| 131 | Whatpm::ContentChecker::FEATURE_STATUS_CR | ||
| 132 | } | ||
| 133 | |||
| 134 | ## NOTE: Diff from HTML 3.2. | ||
| 135 | sub FEATURE_HTML20_RFC () { ## Proposed Standard, obsolete | ||
| 136 | Whatpm::ContentChecker::FEATURE_STATUS_CR | ||
| 137 | } | ||
| 138 | wakaba | 1.58 | |
| 139 | wakaba | 1.29 | ## December 2007 HTML5 Classification |
| 140 | |||
| 141 | my $HTMLMetadataContent = { | ||
| 142 | $HTML_NS => { | ||
| 143 | title => 1, base => 1, link => 1, style => 1, script => 1, noscript => 1, | ||
| 144 | 'event-source' => 1, command => 1, datatemplate => 1, | ||
| 145 | ## NOTE: A |meta| with no |name| element is not allowed as | ||
| 146 | ## a metadata content other than |head| element. | ||
| 147 | meta => 1, | ||
| 148 | wakaba | 1.56 | ## NOTE: Only when empty [WF2] |
| 149 | form => 1, | ||
| 150 | wakaba | 1.29 | }, |
| 151 | ## NOTE: RDF is mentioned in the HTML5 spec. | ||
| 152 | ## TODO: Other RDF elements? | ||
| 153 | q<http://www.w3.org/1999/02/22-rdf-syntax-ns#> => {RDF => 1}, | ||
| 154 | }; | ||
| 155 | |||
| 156 | wakaba | 1.72 | my $HTMLFlowContent = { |
| 157 | wakaba | 1.29 | $HTML_NS => { |
| 158 | section => 1, nav => 1, article => 1, blockquote => 1, aside => 1, | ||
| 159 | h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1, header => 1, | ||
| 160 | footer => 1, address => 1, p => 1, hr => 1, dialog => 1, pre => 1, | ||
| 161 | ol => 1, ul => 1, dl => 1, figure => 1, map => 1, table => 1, | ||
| 162 | wakaba | 1.72 | details => 1, ## ISSUE: "Flow element" in spec. |
| 163 | datagrid => 1, ## ISSUE: "Flow element" in spec. | ||
| 164 | wakaba | 1.29 | datatemplate => 1, |
| 165 | div => 1, ## ISSUE: No category in spec. | ||
| 166 | ## NOTE: |style| is only allowed if |scoped| attribute is specified. | ||
| 167 | ## Additionally, it must be before any other element or | ||
| 168 | ## non-inter-element-whitespace text node. | ||
| 169 | style => 1, | ||
| 170 | |||
| 171 | wakaba | 1.38 | br => 1, q => 1, cite => 1, em => 1, strong => 1, small => 1, mark => 1, |
| 172 | wakaba | 1.29 | dfn => 1, abbr => 1, time => 1, progress => 1, meter => 1, code => 1, |
| 173 | var => 1, samp => 1, kbd => 1, sub => 1, sup => 1, span => 1, i => 1, | ||
| 174 | b => 1, bdo => 1, script => 1, noscript => 1, 'event-source' => 1, | ||
| 175 | command => 1, font => 1, | ||
| 176 | a => 1, | ||
| 177 | datagrid => 1, ## ISSUE: "Interactive element" in the spec. | ||
| 178 | ## NOTE: |area| is allowed only as a descendant of |map|. | ||
| 179 | area => 1, | ||
| 180 | |||
| 181 | ins => 1, del => 1, | ||
| 182 | |||
| 183 | wakaba | 1.72 | ## NOTE: If there is a |menu| ancestor, phrasing. Otherwise, flow. |
| 184 | wakaba | 1.29 | menu => 1, |
| 185 | |||
| 186 | img => 1, iframe => 1, embed => 1, object => 1, video => 1, audio => 1, | ||
| 187 | canvas => 1, | ||
| 188 | }, | ||
| 189 | |||
| 190 | ## NOTE: Embedded | ||
| 191 | q<http://www.w3.org/1998/Math/MathML> => {math => 1}, | ||
| 192 | q<http://www.w3.org/2000/svg> => {svg => 1}, | ||
| 193 | }; | ||
| 194 | |||
| 195 | wakaba | 1.58 | my $HTMLSectioningContent = { |
| 196 | wakaba | 1.57 | $HTML_NS => { |
| 197 | section => 1, nav => 1, article => 1, aside => 1, | ||
| 198 | ## NOTE: |body| is only allowed in |html| element. | ||
| 199 | body => 1, | ||
| 200 | }, | ||
| 201 | }; | ||
| 202 | |||
| 203 | wakaba | 1.58 | my $HTMLSectioningRoot = { |
| 204 | wakaba | 1.29 | $HTML_NS => { |
| 205 | wakaba | 1.58 | blockquote => 1, datagrid => 1, figure => 1, td => 1, |
| 206 | wakaba | 1.29 | }, |
| 207 | }; | ||
| 208 | |||
| 209 | my $HTMLHeadingContent = { | ||
| 210 | $HTML_NS => { | ||
| 211 | h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1, header => 1, | ||
| 212 | }, | ||
| 213 | }; | ||
| 214 | |||
| 215 | my $HTMLPhrasingContent = { | ||
| 216 | wakaba | 1.72 | ## NOTE: All phrasing content is also flow content. |
| 217 | wakaba | 1.29 | $HTML_NS => { |
| 218 | wakaba | 1.38 | br => 1, q => 1, cite => 1, em => 1, strong => 1, small => 1, mark => 1, |
| 219 | wakaba | 1.29 | dfn => 1, abbr => 1, time => 1, progress => 1, meter => 1, code => 1, |
| 220 | var => 1, samp => 1, kbd => 1, sub => 1, sup => 1, span => 1, i => 1, | ||
| 221 | b => 1, bdo => 1, script => 1, noscript => 1, 'event-source' => 1, | ||
| 222 | command => 1, font => 1, | ||
| 223 | a => 1, | ||
| 224 | datagrid => 1, ## ISSUE: "Interactive element" in the spec. | ||
| 225 | ## NOTE: |area| is allowed only as a descendant of |map|. | ||
| 226 | area => 1, | ||
| 227 | |||
| 228 | ## NOTE: Transparent. | ||
| 229 | ins => 1, del => 1, | ||
| 230 | |||
| 231 | wakaba | 1.72 | ## NOTE: If there is a |menu| ancestor, phrasing. Otherwise, flow. |
| 232 | wakaba | 1.29 | menu => 1, |
| 233 | |||
| 234 | img => 1, iframe => 1, embed => 1, object => 1, video => 1, audio => 1, | ||
| 235 | canvas => 1, | ||
| 236 | wakaba | 1.56 | |
| 237 | ## NOTE: WF2 | ||
| 238 | input => 1, ## NOTE: type=hidden | ||
| 239 | datalist => 1, ## NOTE: block | where |select| allowed | ||
| 240 | wakaba | 1.29 | }, |
| 241 | |||
| 242 | ## NOTE: Embedded | ||
| 243 | q<http://www.w3.org/1998/Math/MathML> => {math => 1}, | ||
| 244 | q<http://www.w3.org/2000/svg> => {svg => 1}, | ||
| 245 | |||
| 246 | ## NOTE: And non-inter-element-whitespace text nodes. | ||
| 247 | }; | ||
| 248 | |||
| 249 | wakaba | 1.40 | ## $HTMLEmbeddedContent: See Whatpm::ContentChecker. |
| 250 | wakaba | 1.29 | |
| 251 | my $HTMLInteractiveContent = { | ||
| 252 | $HTML_NS => { | ||
| 253 | a => 1, | ||
| 254 | wakaba | 1.36 | datagrid => 1, ## ISSUE: Categorized as "Inetractive element" |
| 255 | wakaba | 1.29 | }, |
| 256 | }; | ||
| 257 | |||
| 258 | wakaba | 1.36 | ## NOTE: $HTMLTransparentElements: See Whatpm::ContentChecker. |
| 259 | ## NOTE: Semi-transparent elements: See Whatpm::ContentChecker. | ||
| 260 | |||
| 261 | ## -- Common attribute syntacx checkers | ||
| 262 | |||
| 263 | wakaba | 1.1 | our $AttrChecker; |
| 264 | wakaba | 1.82 | our $AttrStatus; |
| 265 | wakaba | 1.1 | |
| 266 | my $GetHTMLEnumeratedAttrChecker = sub { | ||
| 267 | my $states = shift; # {value => conforming ? 1 : -1} | ||
| 268 | return sub { | ||
| 269 | my ($self, $attr) = @_; | ||
| 270 | my $value = lc $attr->value; ## TODO: ASCII case insensitibility? | ||
| 271 | if ($states->{$value} > 0) { | ||
| 272 | # | ||
| 273 | } elsif ($states->{$value}) { | ||
| 274 | $self->{onerror}->(node => $attr, type => 'enumerated:non-conforming'); | ||
| 275 | } else { | ||
| 276 | $self->{onerror}->(node => $attr, type => 'enumerated:invalid'); | ||
| 277 | } | ||
| 278 | }; | ||
| 279 | }; # $GetHTMLEnumeratedAttrChecker | ||
| 280 | |||
| 281 | my $GetHTMLBooleanAttrChecker = sub { | ||
| 282 | my $local_name = shift; | ||
| 283 | return sub { | ||
| 284 | my ($self, $attr) = @_; | ||
| 285 | wakaba | 1.88 | my $value = lc $attr->value; ## TODO: case |
| 286 | wakaba | 1.1 | unless ($value eq $local_name or $value eq '') { |
| 287 | wakaba | 1.88 | $self->{onerror}->(node => $attr, type => 'boolean:invalid', |
| 288 | level => $self->{must_level}); | ||
| 289 | wakaba | 1.1 | } |
| 290 | }; | ||
| 291 | }; # $GetHTMLBooleanAttrChecker | ||
| 292 | |||
| 293 | wakaba | 1.8 | ## Unordered set of space-separated tokens |
| 294 | wakaba | 1.18 | my $HTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker = sub { |
| 295 | wakaba | 1.8 | my ($self, $attr) = @_; |
| 296 | my %word; | ||
| 297 | for my $word (grep {length $_} split /[\x09-\x0D\x20]/, $attr->value) { | ||
| 298 | unless ($word{$word}) { | ||
| 299 | $word{$word} = 1; | ||
| 300 | } else { | ||
| 301 | $self->{onerror}->(node => $attr, type => 'duplicate token:'.$word); | ||
| 302 | } | ||
| 303 | } | ||
| 304 | wakaba | 1.18 | }; # $HTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker |
| 305 | wakaba | 1.8 | |
| 306 | wakaba | 1.1 | ## |rel| attribute (unordered set of space separated tokens, |
| 307 | ## whose allowed values are defined by the section on link types) | ||
| 308 | my $HTMLLinkTypesAttrChecker = sub { | ||
| 309 | wakaba | 1.66 | my ($a_or_area, $todo, $self, $attr, $item, $element_state) = @_; |
| 310 | wakaba | 1.1 | my %word; |
| 311 | for my $word (grep {length $_} split /[\x09-\x0D\x20]/, $attr->value) { | ||
| 312 | unless ($word{$word}) { | ||
| 313 | $word{$word} = 1; | ||
| 314 | wakaba | 1.18 | } elsif ($word eq 'up') { |
| 315 | # | ||
| 316 | wakaba | 1.1 | } else { |
| 317 | $self->{onerror}->(node => $attr, type => 'duplicate token:'.$word); | ||
| 318 | } | ||
| 319 | } | ||
| 320 | ## NOTE: Case sensitive match (since HTML5 spec does not say link | ||
| 321 | ## types are case-insensitive and it says "The value should not | ||
| 322 | ## be confusingly similar to any other defined value (e.g. | ||
| 323 | ## differing only in case)."). | ||
| 324 | ## NOTE: Though there is no explicit "MUST NOT" for undefined values, | ||
| 325 | ## "MAY"s and "only ... MAY" restrict non-standard non-registered | ||
| 326 | ## values to be used conformingly. | ||
| 327 | wakaba | 1.66 | |
| 328 | my $is_hyperlink; | ||
| 329 | my $is_resource; | ||
| 330 | wakaba | 1.1 | require Whatpm::_LinkTypeList; |
| 331 | our $LinkType; | ||
| 332 | for my $word (keys %word) { | ||
| 333 | my $def = $LinkType->{$word}; | ||
| 334 | if (defined $def) { | ||
| 335 | if ($def->{status} eq 'accepted') { | ||
| 336 | if (defined $def->{effect}->[$a_or_area]) { | ||
| 337 | # | ||
| 338 | } else { | ||
| 339 | $self->{onerror}->(node => $attr, | ||
| 340 | type => 'link type:bad context:'.$word); | ||
| 341 | } | ||
| 342 | } elsif ($def->{status} eq 'proposal') { | ||
| 343 | $self->{onerror}->(node => $attr, level => 's', | ||
| 344 | type => 'link type:proposed:'.$word); | ||
| 345 | wakaba | 1.20 | if (defined $def->{effect}->[$a_or_area]) { |
| 346 | # | ||
| 347 | } else { | ||
| 348 | $self->{onerror}->(node => $attr, | ||
| 349 | type => 'link type:bad context:'.$word); | ||
| 350 | } | ||
| 351 | wakaba | 1.1 | } else { # rejected or synonym |
| 352 | $self->{onerror}->(node => $attr, | ||
| 353 | type => 'link type:non-conforming:'.$word); | ||
| 354 | } | ||
| 355 | wakaba | 1.4 | if (defined $def->{effect}->[$a_or_area]) { |
| 356 | if ($word eq 'alternate') { | ||
| 357 | # | ||
| 358 | } elsif ($def->{effect}->[$a_or_area] eq 'hyperlink') { | ||
| 359 | wakaba | 1.66 | $is_hyperlink = 1; |
| 360 | wakaba | 1.4 | } |
| 361 | } | ||
| 362 | wakaba | 1.1 | if ($def->{unique}) { |
| 363 | unless ($self->{has_link_type}->{$word}) { | ||
| 364 | $self->{has_link_type}->{$word} = 1; | ||
| 365 | } else { | ||
| 366 | $self->{onerror}->(node => $attr, | ||
| 367 | type => 'link type:duplicate:'.$word); | ||
| 368 | } | ||
| 369 | } | ||
| 370 | wakaba | 1.66 | |
| 371 | if (defined $def->{effect}->[$a_or_area] and $word ne 'alternate') { | ||
| 372 | $is_hyperlink = 1 if $def->{effect}->[$a_or_area] eq 'hyperlink'; | ||
| 373 | $is_resource = 1 if $def->{effect}->[$a_or_area] eq 'external resource'; | ||
| 374 | } | ||
| 375 | wakaba | 1.1 | } else { |
| 376 | $self->{onerror}->(node => $attr, level => 'unsupported', | ||
| 377 | type => 'link type:'.$word); | ||
| 378 | } | ||
| 379 | } | ||
| 380 | wakaba | 1.66 | $is_hyperlink = 1 if $word{alternate} and not $word{stylesheet}; |
| 381 | wakaba | 1.1 | ## TODO: The Pingback 1.0 specification, which is referenced by HTML5, |
| 382 | ## says that using both X-Pingback: header field and HTML | ||
| 383 | ## <link rel=pingback> is deprecated and if both appears they | ||
| 384 | ## SHOULD contain exactly the same value. | ||
| 385 | ## ISSUE: Pingback 1.0 specification defines the exact representation | ||
| 386 | ## of its link element, which cannot be tested by the current arch. | ||
| 387 | ## ISSUE: Pingback 1.0 specification says that the document MUST NOT | ||
| 388 | ## include any string that matches to the pattern for the rel=pingback link, | ||
| 389 | ## which again inpossible to test. | ||
| 390 | ## ISSUE: rel=pingback href MUST NOT include entities other than predefined 4. | ||
| 391 | wakaba | 1.12 | |
| 392 | ## NOTE: <link rel="up index"><link rel="up up index"> is not an error. | ||
| 393 | wakaba | 1.17 | ## NOTE: We can't check "If the page is part of multiple hierarchies, |
| 394 | ## then they SHOULD be described in different paragraphs.". | ||
| 395 | wakaba | 1.66 | |
| 396 | $todo->{has_hyperlink_link_type} = 1 if $is_hyperlink; | ||
| 397 | if ($is_hyperlink or $a_or_area) { | ||
| 398 | $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1; | ||
| 399 | } | ||
| 400 | if ($is_resource and not $a_or_area) { | ||
| 401 | $element_state->{uri_info}->{href}->{type}->{resource} = 1; | ||
| 402 | } | ||
| 403 | wakaba | 1.1 | }; # $HTMLLinkTypesAttrChecker |
| 404 | wakaba | 1.20 | |
| 405 | ## 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." | ||
| 406 | wakaba | 1.1 | |
| 407 | ## URI (or IRI) | ||
| 408 | my $HTMLURIAttrChecker = sub { | ||
| 409 | wakaba | 1.66 | my ($self, $attr, $item, $element_state) = @_; |
| 410 | wakaba | 1.1 | ## ISSUE: Relative references are allowed? (RFC 3987 "IRI" is an absolute reference with optional fragment identifier.) |
| 411 | my $value = $attr->value; | ||
| 412 | Whatpm::URIChecker->check_iri_reference ($value, sub { | ||
| 413 | my %opt = @_; | ||
| 414 | $self->{onerror}->(node => $attr, level => $opt{level}, | ||
| 415 | type => 'URI::'.$opt{type}. | ||
| 416 | (defined $opt{position} ? ':'.$opt{position} : '')); | ||
| 417 | }); | ||
| 418 | wakaba | 1.17 | $self->{has_uri_attr} = 1; ## TODO: <html manifest> |
| 419 | wakaba | 1.66 | |
| 420 | my $attr_name = $attr->name; | ||
| 421 | $element_state->{uri_info}->{$attr_name}->{node} = $attr; | ||
| 422 | ## TODO: absolute | ||
| 423 | push @{$self->{return}->{uri}->{$value} ||= []}, | ||
| 424 | $element_state->{uri_info}->{$attr_name}; | ||
| 425 | wakaba | 1.1 | }; # $HTMLURIAttrChecker |
| 426 | |||
| 427 | ## A space separated list of one or more URIs (or IRIs) | ||
| 428 | my $HTMLSpaceURIsAttrChecker = sub { | ||
| 429 | my ($self, $attr) = @_; | ||
| 430 | wakaba | 1.66 | |
| 431 | my $type = {ping => 'action', | ||
| 432 | profile => 'namespace', | ||
| 433 | archive => 'resource'}->{$attr->name}; | ||
| 434 | |||
| 435 | wakaba | 1.1 | my $i = 0; |
| 436 | for my $value (split /[\x09-\x0D\x20]+/, $attr->value) { | ||
| 437 | Whatpm::URIChecker->check_iri_reference ($value, sub { | ||
| 438 | my %opt = @_; | ||
| 439 | $self->{onerror}->(node => $attr, level => $opt{level}, | ||
| 440 | wakaba | 1.2 | type => 'URIs:'.':'. |
| 441 | $opt{type}.':'.$i. | ||
| 442 | wakaba | 1.1 | (defined $opt{position} ? ':'.$opt{position} : '')); |
| 443 | }); | ||
| 444 | wakaba | 1.66 | |
| 445 | ## TODO: absolute | ||
| 446 | push @{$self->{return}->{uri}->{$value} ||= []}, | ||
| 447 | wakaba | 1.67 | {node => $attr, type => {$type => 1}}; |
| 448 | wakaba | 1.66 | |
| 449 | wakaba | 1.1 | $i++; |
| 450 | } | ||
| 451 | wakaba | 1.67 | ## ISSUE: Relative references? (especially, in profile="") |
| 452 | wakaba | 1.1 | ## ISSUE: Leading or trailing white spaces are conformant? |
| 453 | ## ISSUE: A sequence of white space characters are conformant? | ||
| 454 | ## ISSUE: A zero-length string is conformant? (It does contain a relative reference, i.e. same as base URI.) | ||
| 455 | ## NOTE: Duplication seems not an error. | ||
| 456 | wakaba | 1.4 | $self->{has_uri_attr} = 1; |
| 457 | wakaba | 1.1 | }; # $HTMLSpaceURIsAttrChecker |
| 458 | |||
| 459 | my $HTMLDatetimeAttrChecker = sub { | ||
| 460 | my ($self, $attr) = @_; | ||
| 461 | my $value = $attr->value; | ||
| 462 | ## ISSUE: "space", not "space character" (in parsing algorihtm, "space character") | ||
| 463 | if ($value =~ /\A([0-9]{4})-([0-9]{2})-([0-9]{2})(?>[\x09-\x0D\x20]+(?>T[\x09-\x0D\x20]*)?|T[\x09-\x0D\x20]*)([0-9]{2}):([0-9]{2})(?>:([0-9]{2}))?(?>\.([0-9]+))?[\x09-\x0D\x20]*(?>Z|[+-]([0-9]{2}):([0-9]{2}))\z/) { | ||
| 464 | my ($y, $M, $d, $h, $m, $s, $f, $zh, $zm) | ||
| 465 | = ($1, $2, $3, $4, $5, $6, $7, $8, $9); | ||
| 466 | if (0 < $M and $M < 13) { ## ISSUE: This is not explicitly specified (though in parsing algorithm) | ||
| 467 | $self->{onerror}->(node => $attr, type => 'datetime:bad day') | ||
| 468 | if $d < 1 or | ||
| 469 | $d > [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]->[$M]; | ||
| 470 | $self->{onerror}->(node => $attr, type => 'datetime:bad day') | ||
| 471 | if $M == 2 and $d == 29 and | ||
| 472 | not ($y % 400 == 0 or ($y % 4 == 0 and $y % 100 != 0)); | ||
| 473 | } else { | ||
| 474 | $self->{onerror}->(node => $attr, type => 'datetime:bad month'); | ||
| 475 | } | ||
| 476 | $self->{onerror}->(node => $attr, type => 'datetime:bad hour') if $h > 23; | ||
| 477 | $self->{onerror}->(node => $attr, type => 'datetime:bad minute') if $m > 59; | ||
| 478 | $self->{onerror}->(node => $attr, type => 'datetime:bad second') | ||
| 479 | if defined $s and $s > 59; | ||
| 480 | $self->{onerror}->(node => $attr, type => 'datetime:bad timezone hour') | ||
| 481 | if $zh > 23; | ||
| 482 | $self->{onerror}->(node => $attr, type => 'datetime:bad timezone minute') | ||
| 483 | if $zm > 59; | ||
| 484 | ## ISSUE: Maybe timezone -00:00 should have same semantics as in RFC 3339. | ||
| 485 | } else { | ||
| 486 | $self->{onerror}->(node => $attr, type => 'datetime:syntax error'); | ||
| 487 | } | ||
| 488 | }; # $HTMLDatetimeAttrChecker | ||
| 489 | |||
| 490 | my $HTMLIntegerAttrChecker = sub { | ||
| 491 | my ($self, $attr) = @_; | ||
| 492 | my $value = $attr->value; | ||
| 493 | unless ($value =~ /\A-?[0-9]+\z/) { | ||
| 494 | $self->{onerror}->(node => $attr, type => 'integer:syntax error'); | ||
| 495 | } | ||
| 496 | }; # $HTMLIntegerAttrChecker | ||
| 497 | |||
| 498 | my $GetHTMLNonNegativeIntegerAttrChecker = sub { | ||
| 499 | my $range_check = shift; | ||
| 500 | return sub { | ||
| 501 | my ($self, $attr) = @_; | ||
| 502 | my $value = $attr->value; | ||
| 503 | if ($value =~ /\A[0-9]+\z/) { | ||
| 504 |