/[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.131 - (hide annotations) (download)
Sat Sep 20 07:00:52 2008 UTC (16 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.130: +2 -1 lines
++ whatpm/t/ChangeLog	20 Sep 2008 07:00:43 -0000
	* content-checker.pl: Remove dedicated parser and adopt
	testfiles.pl parser.

	* content-model-1.dat, content-model-2.dat: Typo fixed.

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

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24