/[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.138 - (hide annotations) (download)
Mon Sep 22 06:48:03 2008 UTC (16 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.137: +17 -3 lines
++ whatpm/t/ChangeLog	22 Sep 2008 06:43:31 -0000
	* content-model-2.dat: Test data on |for=""| attribute are added.

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	22 Sep 2008 06:43:48 -0000
	* HTML.pm: Support for |for=""| validation.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24