/[suikacvs]/markup/html/whatpm/Whatpm/ContentChecker/HTML.pm
Suika

Contents of /markup/html/whatpm/Whatpm/ContentChecker/HTML.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.120 - (hide annotations) (download)
Thu Sep 4 15:01:58 2008 UTC (16 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.119: +29 -23 lines
++ whatpm/Whatpm/ContentChecker/ChangeLog	4 Sep 2008 15:01:55 -0000
	* HTML.pm: RDFa spec status updated.

2008-09-04  Wakaba  <wakaba@suika.fam.cx>

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24