/[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.153 - (hide annotations) (download)
Mon Oct 27 05:44:48 2008 UTC (17 years, 11 months ago) by wakaba
Branch: MAIN
Changes since 1.152: +286 -284 lines
++ whatpm/Whatpm/ContentChecker/ChangeLog	27 Oct 2008 05:44:23 -0000
2008-10-27  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: HTML5 spec status updated, except for
	datatemplate-related features.

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24