/[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.139 - (hide annotations) (download)
Sun Oct 5 05:59:36 2008 UTC (16 years, 9 months ago) by wakaba
Branch: MAIN
Changes since 1.138: +66 -4 lines
++ whatpm/t/ChangeLog	5 Oct 2008 05:38:58 -0000
	* content-model-1.dat, content-model-2.dat: Test results on
	|label| contents fixed.

	* ContentChecker.t: |dom-conformance/html-form-label.dat| added.

	* dom-conformance/: New directory.

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

++ whatpm/t/dom-conformance/ChangeLog	5 Oct 2008 05:35:48 -0000
2008-10-05  Wakaba  <wakaba@suika.fam.cx>

	* html-form-label.dat: New test data file.

	* ChangeLog: New file.

++ whatpm/Whatpm/ChangeLog	5 Oct 2008 05:58:50 -0000
	* ContentChecker.pod: Note on internal flags is added.

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	5 Oct 2008 05:36:36 -0000
2008-10-05  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: <label> content conformance checking implemented.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24