/[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.144 - (hide annotations) (download)
Mon Oct 6 07:54:18 2008 UTC (16 years, 1 month ago) by wakaba
Branch: MAIN
Changes since 1.143: +171 -9 lines
++ whatpm/t/dom-conformance/ChangeLog	6 Oct 2008 07:49:08 -0000
	* html-form-input-1.dat: Tests on remining <input type>-states are
	added.

2008-10-06  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/ContentChecker/ChangeLog	6 Oct 2008 07:48:34 -0000
	* HTML.pm: Checks of attribute applicablity for remaining <input
	type> states are implemented.  Some <input type>-dependent
	attribute checkers implemented.

2008-10-06  Wakaba  <wakaba@suika.fam.cx>

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24