/[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.149 - (hide annotations) (download)
Mon Oct 6 13:41:25 2008 UTC (16 years, 9 months ago) by wakaba
Branch: MAIN
Changes since 1.148: +61 -33 lines
++ whatpm/t/dom-conformance/ChangeLog	6 Oct 2008 12:27:56 -0000
	* html-global-1.dat: A test on <input type=hidden tabindex> is
	added (it was not applied in WF2).

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	6 Oct 2008 13:40:56 -0000
	* HTML.pm: Place checkers for obsolete attributes appropriately.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24