/[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.134 - (hide annotations) (download)
Sun Sep 21 10:40:49 2008 UTC (16 years, 1 month ago) by wakaba
Branch: MAIN
Changes since 1.133: +52 -48 lines
++ whatpm/t/ChangeLog	21 Sep 2008 10:40:43 -0000
	* content-model-1.dat: Test data for |fieldset| |legend| are added.

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	21 Sep 2008 10:40:14 -0000
	* HTML.pm: Support for |fieldset| |legend|.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24