/[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.130 - (hide annotations) (download)
Sat Sep 20 06:10:18 2008 UTC (16 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.129: +77 -31 lines
++ whatpm/t/ChangeLog	20 Sep 2008 05:50:38 -0000
	* content-model-1.dat: Test data for interactive contents are
	added (cf. HTML5 revision 2018).

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

++ whatpm/Whatpm/ChangeLog	20 Sep 2008 05:46:21 -0000
2008-09-20  Wakaba  <wakaba@suika.fam.cx>

	* ContentChecker.pm ($IsInHTMLInteractiveContent): New.

++ whatpm/Whatpm/ContentChecker/ChangeLog	20 Sep 2008 05:51:55 -0000
	* HTML.pm, Atom.pm: Interactrive content implementation synced
	with the spec (HTML5 revision 2018).

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24