/[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.152 - (hide annotations) (download)
Tue Oct 7 11:41:41 2008 UTC (16 years, 9 months ago) by wakaba
Branch: MAIN
Changes since 1.151: +15 -8 lines
++ whatpm/t/ChangeLog	7 Oct 2008 11:39:59 -0000
2008-10-07  Wakaba  <wakaba@suika.fam.cx>

	* content-model-2.dat: Repetition model test is moved to another
	file.  A wrong test result fixed.  Broken test entries fixed.
	<area rev> is now reported as "unknown attribute", since RDFa spec
	adds that attribute to all elements with the Common attribute set.
	Note that we have no plan to support RDFa, at the moment.

++ whatpm/t/dom-conformance/ChangeLog	7 Oct 2008 11:41:37 -0000
	* html-flows-1.dat: <li value> test results updated.

	* html-forms-1.dat: accesskey="" attribute is obsolete.

	* html-metadata-1.dat: Fix broken test results.

	* html-repetitions.dat: The repetition template feature is
	obsolete.  A test entry from ../content-model-2.dat is added.

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

++ whatpm/Whatpm/ChangeLog	7 Oct 2008 11:36:17 -0000
2008-10-07  Wakaba  <wakaba@suika.fam.cx>

	* ContentChecker.pm: New error level "html5_fact" added, which
	should be tentatively used until all of requirements are properly
	specced as RFC 2119 "MUST" in HTML5.

++ whatpm/Whatpm/ContentChecker/ChangeLog	7 Oct 2008 11:37:15 -0000
	* HTML.pm: Quoted-strings in media type specifications were not
	properly decoded.  Fixed support for <li value> with non-<ol>
	parent (or with no parent).

2008-10-07  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 wakaba 1.152 my $v = $3;
683 wakaba 1.1 $v =~ s/\\(.)/$1/gs;
684 wakaba 1.152 push @type, $n => substr ($v, 1, length ($v) - 2);
685 wakaba 1.1 }
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 wakaba 1.152
2806     my $parent_is_ol;
2807 wakaba 1.1 my $parent = $attr->owner_element->manakai_parent_element;
2808     if (defined $parent) {
2809     my $parent_ns = $parent->namespace_uri;
2810     $parent_ns = '' unless defined $parent_ns;
2811     my $parent_ln = $parent->manakai_local_name;
2812 wakaba 1.152 $parent_is_ol = ($parent_ns eq $HTML_NS and $parent_ln eq 'ol');
2813     }
2814    
2815     unless ($parent_is_ol) {
2816     ## ISSUE: No "MUST" in the spec.
2817     $self->{onerror}->(node => $attr,
2818     type => 'non-ol li value',
2819     level => $self->{level}->{html5_fact});
2820 wakaba 1.1 }
2821 wakaba 1.152
2822 wakaba 1.1 $HTMLIntegerAttrChecker->($self, $attr);
2823 wakaba 1.131 },
2824 wakaba 1.49 }, {
2825     %HTMLAttrStatus,
2826 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2827 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2828 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2829 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2830 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2831 wakaba 1.55 #value => FEATURE_HTML5_DEFAULT | FEATURE_XHTMLBASIC11_CR |
2832     # FEATURE_M12N10_REC_DEPRECATED,
2833 wakaba 1.82 value => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED |
2834     FEATURE_XHTMLBASIC11_CR | FEATURE_M12N10_REC,
2835 wakaba 1.1 }),
2836 wakaba 1.40 check_child_element => sub {
2837     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2838     $child_is_transparent, $element_state) = @_;
2839     if ($self->{flag}->{in_menu}) {
2840 wakaba 1.152 ## TODO: In <dir> element, then ...
2841 wakaba 1.40 $HTMLPhrasingContentChecker{check_child_element}->(@_);
2842     } else {
2843 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
2844 wakaba 1.40 }
2845     },
2846     check_child_text => sub {
2847     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2848     if ($self->{flag}->{in_menu}) {
2849 wakaba 1.152 ## TODO: In <dir> element, then ...
2850 wakaba 1.40 $HTMLPhrasingContentChecker{check_child_text}->(@_);
2851 wakaba 1.1 } else {
2852 wakaba 1.72 $HTMLFlowContentChecker{check_child_text}->(@_);
2853 wakaba 1.1 }
2854     },
2855     };
2856    
2857     $Element->{$HTML_NS}->{dl} = {
2858 wakaba 1.40 %HTMLChecker,
2859 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2860 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2861     compact => $GetHTMLBooleanAttrChecker->('compact'),
2862     }, {
2863 wakaba 1.49 %HTMLAttrStatus,
2864 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2865 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2866 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2867 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2868     sdapref => FEATURE_HTML20_RFC,
2869 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2870     }),
2871 wakaba 1.40 check_start => sub {
2872     my ($self, $item, $element_state) = @_;
2873     $element_state->{phase} = 'before dt';
2874 wakaba 1.79
2875     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2876     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2877 wakaba 1.40 },
2878     check_child_element => sub {
2879     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2880     $child_is_transparent, $element_state) = @_;
2881 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
2882     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
2883 wakaba 1.40 $self->{onerror}->(node => $child_el,
2884     type => 'element not allowed:minus',
2885 wakaba 1.104 level => $self->{level}->{must});
2886 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2887     #
2888     } elsif ($element_state->{phase} eq 'in dds') {
2889     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2890     #$element_state->{phase} = 'in dds';
2891     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2892     $element_state->{phase} = 'in dts';
2893     } else {
2894 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2895     level => $self->{level}->{must});
2896 wakaba 1.40 }
2897     } elsif ($element_state->{phase} eq 'in dts') {
2898     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2899     #$element_state->{phase} = 'in dts';
2900     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2901     $element_state->{phase} = 'in dds';
2902     } else {
2903 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2904     level => $self->{level}->{must});
2905 wakaba 1.40 }
2906     } elsif ($element_state->{phase} eq 'before dt') {
2907     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2908     $element_state->{phase} = 'in dts';
2909     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2910     $self->{onerror}
2911 wakaba 1.104 ->(node => $child_el, type => 'ps element missing',
2912     text => 'dt',
2913     level => $self->{level}->{must});
2914 wakaba 1.40 $element_state->{phase} = 'in dds';
2915     } else {
2916 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2917     level => $self->{level}->{must});
2918 wakaba 1.1 }
2919 wakaba 1.40 } else {
2920     die "check_child_element: Bad |dl| phase: $element_state->{phase}";
2921 wakaba 1.1 }
2922 wakaba 1.40 },
2923     check_child_text => sub {
2924     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2925     if ($has_significant) {
2926 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
2927     level => $self->{level}->{must});
2928 wakaba 1.40 }
2929     },
2930     check_end => sub {
2931     my ($self, $item, $element_state) = @_;
2932     if ($element_state->{phase} eq 'in dts') {
2933     $self->{onerror}->(node => $item->{node},
2934 wakaba 1.104 type => 'child element missing',
2935     text => 'dd',
2936     level => $self->{level}->{must});
2937 wakaba 1.1 }
2938    
2939 wakaba 1.40 $HTMLChecker{check_end}->(@_);
2940 wakaba 1.1 },
2941     };
2942    
2943     $Element->{$HTML_NS}->{dt} = {
2944 wakaba 1.40 %HTMLPhrasingContentChecker,
2945 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2946 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2947     %HTMLAttrStatus,
2948 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2949 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2950 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2951 wakaba 1.49 }),
2952 wakaba 1.1 };
2953    
2954     $Element->{$HTML_NS}->{dd} = {
2955 wakaba 1.72 %HTMLFlowContentChecker,
2956 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2957 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2958     %HTMLAttrStatus,
2959 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2960 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2961 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2962 wakaba 1.49 }),
2963 wakaba 1.1 };
2964    
2965     $Element->{$HTML_NS}->{a} = {
2966 wakaba 1.123 %HTMLTransparentChecker,
2967 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2968 wakaba 1.40 check_attrs => sub {
2969     my ($self, $item, $element_state) = @_;
2970 wakaba 1.1 my %attr;
2971 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
2972 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
2973     $attr_ns = '' unless defined $attr_ns;
2974     my $attr_ln = $attr->manakai_local_name;
2975     my $checker;
2976 wakaba 1.73 my $status;
2977 wakaba 1.1 if ($attr_ns eq '') {
2978 wakaba 1.73 $status = {
2979     %HTMLAttrStatus,
2980 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2981 wakaba 1.73 accesskey => FEATURE_M12N10_REC,
2982     charset => FEATURE_M12N10_REC,
2983 wakaba 1.82 coords => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2984 wakaba 1.73 cryptopts => FEATURE_RFC2659,
2985     dn => FEATURE_RFC2659,
2986 wakaba 1.121 href => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_CR | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2987 wakaba 1.82 hreflang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2988 wakaba 1.73 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2989 wakaba 1.82 media => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED,
2990 wakaba 1.73 methods => FEATURE_HTML20_RFC,
2991     name => FEATURE_M12N10_REC_DEPRECATED,
2992     nonce => FEATURE_RFC2659,
2993     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2994     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2995     ping => FEATURE_HTML5_DEFAULT,
2996 wakaba 1.120 rel => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_CR | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2997     rev => FEATURE_RDFA_CR | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2998 wakaba 1.73 sdapref => FEATURE_HTML20_RFC,
2999 wakaba 1.82 shape => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3000 wakaba 1.73 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3001 wakaba 1.82 target => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3002 wakaba 1.73 type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3003     urn => FEATURE_HTML20_RFC,
3004     }->{$attr_ln};
3005    
3006 wakaba 1.1 $checker = {
3007 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
3008 wakaba 1.91 charset => sub {
3009     my ($self, $attr) = @_;
3010     $HTMLCharsetChecker->($attr->value, @_);
3011     },
3012 wakaba 1.70 ## TODO: HTML4 |coords|
3013 wakaba 1.1 target => $HTMLTargetAttrChecker,
3014     href => $HTMLURIAttrChecker,
3015     ping => $HTMLSpaceURIsAttrChecker,
3016 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) },
3017 wakaba 1.92 rev => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(),
3018 wakaba 1.70 ## TODO: HTML4 |shape|
3019 wakaba 1.1 media => $HTMLMQAttrChecker,
3020 wakaba 1.70 ## TODO: HTML4/XHTML1 |name|
3021 wakaba 1.1 hreflang => $HTMLLanguageTagAttrChecker,
3022     type => $HTMLIMTAttrChecker,
3023     }->{$attr_ln};
3024     if ($checker) {
3025     $attr{$attr_ln} = $attr;
3026 wakaba 1.122 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and
3027     $attr_ln !~ /[A-Z]/) {
3028 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
3029     $status = $HTMLDatasetAttrStatus;
3030 wakaba 1.1 } else {
3031     $checker = $HTMLAttrChecker->{$attr_ln};
3032     }
3033     }
3034     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
3035     || $AttrChecker->{$attr_ns}->{''};
3036 wakaba 1.82 $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
3037     || $AttrStatus->{$attr_ns}->{''};
3038     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
3039 wakaba 1.62
3040 wakaba 1.1 if ($checker) {
3041 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
3042 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
3043 wakaba 1.54 #
3044 wakaba 1.1 } else {
3045 wakaba 1.104 $self->{onerror}->(node => $attr,
3046     type => 'unknown attribute',
3047     level => $self->{level}->{uncertain});
3048 wakaba 1.50 ## ISSUE: No conformance createria for unknown attributes in the spec
3049 wakaba 1.1 }
3050 wakaba 1.49
3051 wakaba 1.82 $self->_attr_status_info ($attr, $status);
3052 wakaba 1.1 }
3053    
3054 wakaba 1.40 $element_state->{in_a_href_original} = $self->{flag}->{in_a_href};
3055 wakaba 1.4 if (defined $attr{href}) {
3056     $self->{has_hyperlink_element} = 1;
3057 wakaba 1.40 $self->{flag}->{in_a_href} = 1;
3058 wakaba 1.4 } else {
3059 wakaba 1.1 for (qw/target ping rel media hreflang type/) {
3060     if (defined $attr{$_}) {
3061     $self->{onerror}->(node => $attr{$_},
3062 wakaba 1.104 type => 'attribute not allowed',
3063     level => $self->{level}->{must});
3064 wakaba 1.1 }
3065     }
3066     }
3067 wakaba 1.66
3068     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
3069 wakaba 1.1 },
3070 wakaba 1.40 check_start => sub {
3071     my ($self, $item, $element_state) = @_;
3072     $self->_add_minus_elements ($element_state, $HTMLInteractiveContent);
3073 wakaba 1.79
3074     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3075     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3076 wakaba 1.40 },
3077     check_end => sub {
3078     my ($self, $item, $element_state) = @_;
3079     $self->_remove_minus_elements ($element_state);
3080 wakaba 1.59 delete $self->{flag}->{in_a_href}
3081     unless $element_state->{in_a_href_original};
3082 wakaba 1.1
3083 wakaba 1.123 $HTMLTransparentChecker{check_end}->(@_);
3084 wakaba 1.1 },
3085     };
3086    
3087     $Element->{$HTML_NS}->{q} = {
3088 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3089 wakaba 1.40 %HTMLPhrasingContentChecker,
3090     check_attrs => $GetHTMLAttrsChecker->({
3091 wakaba 1.50 cite => $HTMLURIAttrChecker,
3092     }, {
3093 wakaba 1.49 %HTMLAttrStatus,
3094 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3095     cite => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3096 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3097 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
3098     sdasuff => FEATURE_HTML2X_RFC,
3099 wakaba 1.1 }),
3100 wakaba 1.66 check_start => sub {
3101     my ($self, $item, $element_state) = @_;
3102    
3103     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
3104 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3105     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3106 wakaba 1.66 },
3107 wakaba 1.1 };
3108 wakaba 1.75 ## TODO: "Quotation punctuation (such as quotation marks), if any, must be
3109     ## placed inside the <code>q</code> element." Though we cannot test the
3110     ## element against this requirement since it incluides a semantic bit,
3111     ## it might be possible to inform of the existence of quotation marks OUTSIDE
3112     ## the |q| element.
3113 wakaba 1.1
3114     $Element->{$HTML_NS}->{cite} = {
3115 wakaba 1.40 %HTMLPhrasingContentChecker,
3116 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3117 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3118     %HTMLAttrStatus,
3119 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3120 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3121 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3122 wakaba 1.49 }),
3123 wakaba 1.1 };
3124    
3125     $Element->{$HTML_NS}->{em} = {
3126 wakaba 1.40 %HTMLPhrasingContentChecker,
3127 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3128 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3129     %HTMLAttrStatus,
3130 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3131 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3132 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3133 wakaba 1.49 }),
3134 wakaba 1.1 };
3135    
3136     $Element->{$HTML_NS}->{strong} = {
3137 wakaba 1.40 %HTMLPhrasingContentChecker,
3138 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3139 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3140     %HTMLAttrStatus,
3141 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3142 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3143 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3144 wakaba 1.49 }),
3145 wakaba 1.1 };
3146    
3147     $Element->{$HTML_NS}->{small} = {
3148 wakaba 1.40 %HTMLPhrasingContentChecker,
3149 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3150     check_attrs => $GetHTMLAttrsChecker->({}, {
3151     %HTMLAttrStatus,
3152     %HTMLM12NCommonAttrStatus,
3153 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3154 wakaba 1.49 }),
3155 wakaba 1.1 };
3156    
3157 wakaba 1.51 $Element->{$HTML_NS}->{big} = {
3158     %HTMLPhrasingContentChecker,
3159     status => FEATURE_M12N10_REC,
3160     check_attrs => $GetHTMLAttrsChecker->({}, {
3161     %HTMLAttrStatus,
3162     %HTMLM12NCommonAttrStatus,
3163     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3164     }),
3165     };
3166    
3167 wakaba 1.38 $Element->{$HTML_NS}->{mark} = {
3168 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3169 wakaba 1.40 %HTMLPhrasingContentChecker,
3170 wakaba 1.1 };
3171    
3172     $Element->{$HTML_NS}->{dfn} = {
3173 wakaba 1.40 %HTMLPhrasingContentChecker,
3174 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3175 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3176     %HTMLAttrStatus,
3177 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3178 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3179 wakaba 1.49 }),
3180 wakaba 1.40 check_start => sub {
3181     my ($self, $item, $element_state) = @_;
3182     $self->_add_minus_elements ($element_state, {$HTML_NS => {dfn => 1}});
3183 wakaba 1.1
3184 wakaba 1.40 my $node = $item->{node};
3185 wakaba 1.1 my $term = $node->get_attribute_ns (undef, 'title');
3186     unless (defined $term) {
3187     for my $child (@{$node->child_nodes}) {
3188     if ($child->node_type == 1) { # ELEMENT_NODE
3189     if (defined $term) {
3190     undef $term;
3191     last;
3192     } elsif ($child->manakai_local_name eq 'abbr') {
3193     my $nsuri = $child->namespace_uri;
3194     if (defined $nsuri and $nsuri eq $HTML_NS) {
3195     my $attr = $child->get_attribute_node_ns (undef, 'title');
3196     if ($attr) {
3197     $term = $attr->value;
3198     }
3199     }
3200     }
3201     } elsif ($child->node_type == 3 or $child->node_type == 4) {
3202     ## TEXT_NODE or CDATA_SECTION_NODE
3203 wakaba 1.132 if ($child->data =~ /\A[\x09\x0A\x0C\x0D\x20]+\z/) { # Inter-element whitespace
3204 wakaba 1.1 next;
3205     }
3206     undef $term;
3207     last;
3208     }
3209     }
3210     unless (defined $term) {
3211     $term = $node->text_content;
3212     }
3213     }
3214     if ($self->{term}->{$term}) {
3215     push @{$self->{term}->{$term}}, $node;
3216     } else {
3217     $self->{term}->{$term} = [$node];
3218     }
3219 wakaba 1.77 ## ISSUE: The HTML5 definition for the defined term does not work with
3220     ## |ruby| unless |dfn| has |title|.
3221 wakaba 1.79
3222     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3223     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3224 wakaba 1.40 },
3225     check_end => sub {
3226     my ($self, $item, $element_state) = @_;
3227     $self->_remove_minus_elements ($element_state);
3228 wakaba 1.1
3229 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
3230 wakaba 1.1 },
3231     };
3232    
3233     $Element->{$HTML_NS}->{abbr} = {
3234 wakaba 1.40 %HTMLPhrasingContentChecker,
3235 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3236 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3237     %HTMLAttrStatus,
3238 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3239     full => FEATURE_XHTML2_ED,
3240 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3241 wakaba 1.49 }),
3242 wakaba 1.77 ## NOTE: "If an abbreviation is pluralised, the expansion's grammatical
3243     ## number (plural vs singular) must match the grammatical number of the
3244     ## contents of the element." Though this can be checked by machine,
3245     ## it requires language-specific knowledge and dictionary, such that
3246     ## we don't support the check of the requirement.
3247     ## ISSUE: Is <abbr title="Cascading Style Sheets">CSS</abbr> conforming?
3248 wakaba 1.49 };
3249    
3250     $Element->{$HTML_NS}->{acronym} = {
3251     %HTMLPhrasingContentChecker,
3252     status => FEATURE_M12N10_REC,
3253     check_attrs => $GetHTMLAttrsChecker->({}, {
3254     %HTMLAttrStatus,
3255     %HTMLM12NCommonAttrStatus,
3256 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3257 wakaba 1.49 }),
3258 wakaba 1.1 };
3259    
3260     $Element->{$HTML_NS}->{time} = {
3261 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3262 wakaba 1.40 %HTMLPhrasingContentChecker,
3263     check_attrs => $GetHTMLAttrsChecker->({
3264 wakaba 1.1 datetime => sub { 1 }, # checked in |checker|
3265 wakaba 1.49 }, {
3266     %HTMLAttrStatus,
3267     %HTMLM12NCommonAttrStatus,
3268 wakaba 1.72 datetime => FEATURE_HTML5_FD,
3269 wakaba 1.1 }),
3270     ## TODO: Write tests
3271 wakaba 1.40 check_end => sub {
3272     my ($self, $item, $element_state) = @_;
3273 wakaba 1.1
3274 wakaba 1.40 my $attr = $item->{node}->get_attribute_node_ns (undef, 'datetime');
3275 wakaba 1.1 my $input;
3276     my $reg_sp;
3277     my $input_node;
3278     if ($attr) {
3279     $input = $attr->value;
3280 wakaba 1.132 $reg_sp = qr/[\x09\x0A\x0C\x0D\x20]*/;
3281 wakaba 1.1 $input_node = $attr;
3282     } else {
3283 wakaba 1.40 $input = $item->{node}->text_content;
3284 wakaba 1.112 $reg_sp = qr/\p{WhiteSpace}*/;
3285 wakaba 1.40 $input_node = $item->{node};
3286 wakaba 1.1
3287     ## ISSUE: What is the definition for "successfully extracts a date
3288     ## or time"? If the algorithm says the string is invalid but
3289     ## return some date or time, is it "successfully"?
3290     }
3291    
3292     my $hour;
3293     my $minute;
3294     my $second;
3295     if ($input =~ /
3296     \A
3297 wakaba 1.112 $reg_sp
3298 wakaba 1.1 ([0-9]+) # 1
3299     (?>
3300     -([0-9]+) # 2
3301 wakaba 1.112 -((?>[0-9]+)) # 3 # Use (?>) such that yyyy-mm-ddhh:mm does not match
3302     $reg_sp
3303 wakaba 1.1 (?>
3304     T
3305 wakaba 1.112 $reg_sp
3306 wakaba 1.1 )?
3307     ([0-9]+) # 4
3308     :([0-9]+) # 5
3309     (?>
3310     :([0-9]+(?>\.[0-9]*)?|\.[0-9]*) # 6
3311     )?
3312 wakaba 1.112 $reg_sp
3313 wakaba 1.1 (?>
3314     Z
3315 wakaba 1.112 $reg_sp
3316 wakaba 1.1 |
3317     [+-]([0-9]+):([0-9]+) # 7, 8
3318 wakaba 1.112 $reg_sp
3319 wakaba 1.1 )?
3320     \z
3321     |
3322     :([0-9]+) # 9
3323     (?>
3324     :([0-9]+(?>\.[0-9]*)?|\.[0-9]*) # 10
3325     )?
3326 wakaba 1.112 $reg_sp
3327     \z
3328 wakaba 1.1 )
3329     /x) {
3330     if (defined $2) { ## YYYY-MM-DD T? hh:mm
3331     if (length $1 != 4 or length $2 != 2 or length $3 != 2 or
3332     length $4 != 2 or length $5 != 2) {
3333     $self->{onerror}->(node => $input_node,
3334 wakaba 1.104 type => 'dateortime:syntax error',
3335     level => $self->{level}->{must});
3336 wakaba 1.1 }
3337    
3338     if (1 <= $2 and $2 <= 12) {
3339 wakaba 1.104 $self->{onerror}->(node => $input_node, type => 'datetime:bad day',
3340     level => $self->{level}->{must})
3341 wakaba 1.1 if $3 < 1 or
3342     $3 > [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]->[$2];
3343 wakaba 1.104 $self->{onerror}->(node => $input_node, type => 'datetime:bad day',
3344     level => $self->{level}->{must})
3345 wakaba 1.1 if $2 == 2 and $3 == 29 and
3346     not ($1 % 400 == 0 or ($1 % 4 == 0 and $1 % 100 != 0));
3347     } else {
3348     $self->{onerror}->(node => $input_node,
3349 wakaba 1.104 type => 'datetime:bad month',
3350     level => $self->{level}->{must});
3351 wakaba 1.1 }
3352    
3353     ($hour, $minute, $second) = ($4, $5, $6);
3354    
3355     if (defined $7) { ## [+-]hh:mm
3356     if (length $7 != 2 or length $8 != 2) {
3357     $self->{onerror}->(node => $input_node,
3358 wakaba 1.104 type => 'dateortime:syntax error',
3359     level => $self->{level}->{must});
3360 wakaba 1.1 }
3361    
3362     $self->{onerror}->(node => $input_node,
3363 wakaba 1.104 type => 'datetime:bad timezone hour',
3364     level => $self->{level}->{must})
3365 wakaba 1.1 if $7 > 23;
3366     $self->{onerror}->(node => $input_node,
3367 wakaba 1.104 type => 'datetime:bad timezone minute',
3368     level => $self->{level}->{must})
3369 wakaba 1.1 if $8 > 59;
3370     }
3371     } else { ## hh:mm
3372     if (length $1 != 2 or length $9 != 2) {
3373     $self->{onerror}->(node => $input_node,
3374 wakaba 1.104 type => qq'dateortime:syntax error',
3375     level => $self->{level}->{must});
3376 wakaba 1.1 }
3377    
3378     ($hour, $minute, $second) = ($1, $9, $10);
3379     }
3380    
3381 wakaba 1.104 $self->{onerror}->(node => $input_node, type => 'datetime:bad hour',
3382     level => $self->{level}->{must}) if $hour > 23;
3383     $self->{onerror}->(node => $input_node, type => 'datetime:bad minute',
3384     level => $self->{level}->{must}) if $minute > 59;
3385 wakaba 1.1
3386     if (defined $second) { ## s
3387     ## NOTE: Integer part of second don't have to have length of two.
3388    
3389     if (substr ($second, 0, 1) eq '.') {
3390     $self->{onerror}->(node => $input_node,
3391 wakaba 1.104 type => 'dateortime:syntax error',
3392     level => $self->{level}->{must});
3393 wakaba 1.1 }
3394    
3395 wakaba 1.104 $self->{onerror}->(node => $input_node, type => 'datetime:bad second',
3396     level => $self->{level}->{must}) if $second >= 60;
3397 wakaba 1.1 }
3398     } else {
3399     $self->{onerror}->(node => $input_node,
3400 wakaba 1.104 type => 'dateortime:syntax error',
3401     level => $self->{level}->{must});
3402 wakaba 1.1 }
3403    
3404 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
3405 wakaba 1.1 },
3406     };
3407    
3408     $Element->{$HTML_NS}->{meter} = { ## TODO: "The recommended way of giving the value is to include it as contents of the element"
3409 wakaba 1.77 ## TODO: value inequalities (HTML5 revision 1463)
3410 wakaba 1.113 ## TODO: content checking
3411     ## TODO: content or value must contain number (rev 2053)
3412 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3413 wakaba 1.40 %HTMLPhrasingContentChecker,
3414     check_attrs => $GetHTMLAttrsChecker->({
3415 wakaba 1.1 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3416     min => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3417     low => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3418     high => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3419     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3420     optimum => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3421 wakaba 1.50 }, {
3422     %HTMLAttrStatus,
3423     high => FEATURE_HTML5_DEFAULT,
3424     low => FEATURE_HTML5_DEFAULT,
3425     max => FEATURE_HTML5_DEFAULT,
3426     min => FEATURE_HTML5_DEFAULT,
3427     optimum => FEATURE_HTML5_DEFAULT,
3428     value => FEATURE_HTML5_DEFAULT,
3429 wakaba 1.1 }),
3430     };
3431    
3432     $Element->{$HTML_NS}->{progress} = { ## TODO: recommended to use content
3433 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3434 wakaba 1.40 %HTMLPhrasingContentChecker,
3435     check_attrs => $GetHTMLAttrsChecker->({
3436 wakaba 1.1 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift >= 0 }),
3437     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift > 0 }),
3438 wakaba 1.50 }, {
3439     %HTMLAttrStatus,
3440     max => FEATURE_HTML5_DEFAULT,
3441     value => FEATURE_HTML5_DEFAULT,
3442 wakaba 1.1 }),
3443     };
3444    
3445     $Element->{$HTML_NS}->{code} = {
3446 wakaba 1.40 %HTMLPhrasingContentChecker,
3447 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3448 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3449     %HTMLAttrStatus,
3450 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3451 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3452 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3453 wakaba 1.49 }),
3454 wakaba 1.1 };
3455    
3456     $Element->{$HTML_NS}->{var} = {
3457 wakaba 1.40 %HTMLPhrasingContentChecker,
3458 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3459 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3460     %HTMLAttrStatus,
3461 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3462 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3463 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3464 wakaba 1.49 }),
3465 wakaba 1.1 };
3466    
3467     $Element->{$HTML_NS}->{samp} = {
3468 wakaba 1.40 %HTMLPhrasingContentChecker,
3469 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3470 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3471     %HTMLAttrStatus,
3472 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3473 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3474 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3475 wakaba 1.49 }),
3476 wakaba 1.1 };
3477    
3478     $Element->{$HTML_NS}->{kbd} = {
3479 wakaba 1.40 %HTMLPhrasingContentChecker,
3480 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3481 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3482     %HTMLAttrStatus,
3483 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3484 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3485 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3486 wakaba 1.49 }),
3487 wakaba 1.1 };
3488    
3489     $Element->{$HTML_NS}->{sub} = {
3490 wakaba 1.40 %HTMLPhrasingContentChecker,
3491 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3492 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3493     %HTMLAttrStatus,
3494 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3495 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3496 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
3497 wakaba 1.49 }),
3498 wakaba 1.1 };
3499    
3500 wakaba 1.51 $Element->{$HTML_NS}->{sup} = $Element->{$HTML_NS}->{sub};
3501 wakaba 1.1
3502     $Element->{$HTML_NS}->{span} = {
3503 wakaba 1.40 %HTMLPhrasingContentChecker,
3504 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3505 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3506     %HTMLAttrStatus,
3507 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3508 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
3509     dataformatas => FEATURE_HTML4_REC_RESERVED,
3510     datasrc => FEATURE_HTML4_REC_RESERVED,
3511 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3512 wakaba 1.61 sdaform => FEATURE_HTML2X_RFC,
3513 wakaba 1.49 }),
3514 wakaba 1.1 };
3515    
3516     $Element->{$HTML_NS}->{i} = {
3517 wakaba 1.40 %HTMLPhrasingContentChecker,
3518 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3519     check_attrs => $GetHTMLAttrsChecker->({}, {
3520     %HTMLAttrStatus,
3521     %HTMLM12NCommonAttrStatus,
3522 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3523 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3524 wakaba 1.49 }),
3525 wakaba 1.1 };
3526    
3527 wakaba 1.51 $Element->{$HTML_NS}->{b} = $Element->{$HTML_NS}->{i};
3528    
3529 wakaba 1.61 $Element->{$HTML_NS}->{tt} = {
3530     %HTMLPhrasingContentChecker,
3531     status => FEATURE_M12N10_REC,
3532     check_attrs => $GetHTMLAttrsChecker->({}, {
3533     %HTMLAttrStatus,
3534     %HTMLM12NCommonAttrStatus,
3535     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3536     sdaform => FEATURE_HTML20_RFC,
3537     }),
3538     };
3539 wakaba 1.51
3540     $Element->{$HTML_NS}->{s} = {
3541 wakaba 1.40 %HTMLPhrasingContentChecker,
3542 wakaba 1.51 status => FEATURE_M12N10_REC_DEPRECATED,
3543 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3544     %HTMLAttrStatus,
3545     %HTMLM12NCommonAttrStatus,
3546 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3547 wakaba 1.49 }),
3548 wakaba 1.1 };
3549    
3550 wakaba 1.51 $Element->{$HTML_NS}->{strike} = $Element->{$HTML_NS}->{s};
3551    
3552     $Element->{$HTML_NS}->{u} = $Element->{$HTML_NS}->{s};
3553    
3554 wakaba 1.1 $Element->{$HTML_NS}->{bdo} = {
3555 wakaba 1.40 %HTMLPhrasingContentChecker,
3556 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3557 wakaba 1.40 check_attrs => sub {
3558     my ($self, $item, $element_state) = @_;
3559 wakaba 1.49 $GetHTMLAttrsChecker->({}, {
3560     %HTMLAttrStatus,
3561 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3562     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3563     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3564 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3565 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3566     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3567 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
3568     sdasuff => FEATURE_HTML2X_RFC,
3569 wakaba 1.49 })->($self, $item, $element_state);
3570 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'dir')) {
3571     $self->{onerror}->(node => $item->{node},
3572 wakaba 1.104 type => 'attribute missing',
3573     text => 'dir',
3574     level => $self->{level}->{must});
3575 wakaba 1.1 }
3576     },
3577     ## ISSUE: The spec does not directly say that |dir| is a enumerated attr.
3578     };
3579    
3580 wakaba 1.99 $Element->{$HTML_NS}->{ruby} = {
3581     %HTMLPhrasingContentChecker,
3582     status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC,
3583     check_attrs => $GetHTMLAttrsChecker->({}, {
3584     %HTMLAttrStatus,
3585     %HTMLM12NXHTML2CommonAttrStatus, # XHTML 1.1 & XHTML 2.0 & XHTML+RDFa 1.0
3586     lang => FEATURE_HTML5_DEFAULT,
3587     }),
3588     check_start => sub {
3589     my ($self, $item, $element_state) = @_;
3590    
3591     $element_state->{phase} = 'before-rb';
3592     #$element_state->{has_sig}
3593 wakaba 1.100
3594     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3595     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3596 wakaba 1.99 },
3597     ## NOTE: (phrasing, (rt | (rp, rt, rp)))+
3598     check_child_element => sub {
3599     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3600     $child_is_transparent, $element_state) = @_;
3601 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
3602     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
3603 wakaba 1.99 $self->{onerror}->(node => $child_el,
3604     type => 'element not allowed:minus',
3605 wakaba 1.104 level => $self->{level}->{must});
3606 wakaba 1.99 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3607     #
3608     } elsif ($element_state->{phase} eq 'before-rb') {
3609     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3610     $element_state->{phase} = 'in-rb';
3611     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3612     $self->{onerror}->(node => $child_el,
3613 wakaba 1.104 level => $self->{level}->{should},
3614     type => 'no significant content before');
3615 wakaba 1.99 $element_state->{phase} = 'after-rt';
3616     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3617     $self->{onerror}->(node => $child_el,
3618 wakaba 1.104 level => $self->{level}->{should},
3619     type => 'no significant content before');
3620 wakaba 1.99 $element_state->{phase} = 'after-rp1';
3621     } else {
3622     $self->{onerror}->(node => $child_el,
3623 wakaba 1.104 type => 'element not allowed:ruby base',
3624     level => $self->{level}->{must});
3625 wakaba 1.99 $element_state->{phase} = 'in-rb';
3626     }
3627     } elsif ($element_state->{phase} eq 'in-rb') {
3628     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3629     #$element_state->{phase} = 'in-rb';
3630     } elsif ($child_ln eq 'rt' 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-rt';
3637     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3638     unless ($element_state->{has_significant}) {
3639     $self->{onerror}->(node => $child_el,
3640 wakaba 1.104 level => $self->{level}->{should},
3641     type => 'no significant content before');
3642 wakaba 1.99 }
3643     $element_state->{phase} = 'after-rp1';
3644     } else {
3645     $self->{onerror}->(node => $child_el,
3646 wakaba 1.104 type => 'element not allowed:ruby base',
3647     level => $self->{level}->{must});
3648 wakaba 1.99 #$element_state->{phase} = 'in-rb';
3649     }
3650     } elsif ($element_state->{phase} eq 'after-rt') {
3651     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3652     if ($element_state->{has_significant}) {
3653     $element_state->{has_sig} = 1;
3654     delete $element_state->{has_significant};
3655     }
3656     $element_state->{phase} = 'in-rb';
3657     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3658     $self->{onerror}->(node => $child_el,
3659 wakaba 1.104 level => $self->{level}->{should},
3660     type => 'no significant content before');
3661 wakaba 1.99 $element_state->{phase} = 'after-rp1';
3662     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3663     $self->{onerror}->(node => $child_el,
3664 wakaba 1.104 level => $self->{level}->{should},
3665     type => 'no significant content before');
3666 wakaba 1.99 #$element_state->{phase} = 'after-rt';
3667     } else {
3668     $self->{onerror}->(node => $child_el,
3669 wakaba 1.104 type => 'element not allowed:ruby base',
3670     level => $self->{level}->{must});
3671 wakaba 1.99 if ($element_state->{has_significant}) {
3672     $element_state->{has_sig} = 1;
3673     delete $element_state->{has_significant};
3674     }
3675     $element_state->{phase} = 'in-rb';
3676     }
3677     } elsif ($element_state->{phase} eq 'after-rp1') {
3678     if ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3679     $element_state->{phase} = 'after-rp-rt';
3680     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3681     $self->{onerror}->(node => $child_el,
3682 wakaba 1.104 type => 'ps element missing',
3683     text => 'rt',
3684     level => $self->{level}->{must});
3685 wakaba 1.99 $element_state->{phase} = 'after-rp2';
3686     } else {
3687     $self->{onerror}->(node => $child_el,
3688 wakaba 1.104 type => 'ps element missing',
3689     text => 'rt',
3690     level => $self->{level}->{must});
3691 wakaba 1.99 $self->{onerror}->(node => $child_el,
3692 wakaba 1.104 type => 'ps element missing',
3693     text => 'rp',
3694     level => $self->{level}->{must});
3695 wakaba 1.99 unless ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3696     $self->{onerror}->(node => $child_el,
3697 wakaba 1.104 type => 'element not allowed:ruby base',
3698     level => $self->{level}->{must});
3699 wakaba 1.99 }
3700     if ($element_state->{has_significant}) {
3701     $element_state->{has_sig} = 1;
3702     delete $element_state->{has_significant};
3703     }
3704     $element_state->{phase} = 'in-rb';
3705     }
3706     } elsif ($element_state->{phase} eq 'after-rp-rt') {
3707     if ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3708     $element_state->{phase} = 'after-rp2';
3709     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3710     $self->{onerror}->(node => $child_el,
3711 wakaba 1.104 type => 'ps element missing',
3712     text => 'rp',
3713     level => $self->{level}->{must});
3714 wakaba 1.99 $self->{onerror}->(node => $child_el,
3715 wakaba 1.104 level => $self->{level}->{should},
3716     type => 'no significant content before');
3717 wakaba 1.99 $element_state->{phase} = 'after-rt';
3718     } else {
3719     $self->{onerror}->(node => $child_el,
3720 wakaba 1.104 type => 'ps element missing',
3721     text => 'rp',
3722     level => $self->{level}->{must});
3723 wakaba 1.99 unless ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3724     $self->{onerror}->(node => $child_el,
3725 wakaba 1.104 type => 'element not allowed:ruby base',
3726     level => $self->{level}->{must});
3727 wakaba 1.99 }
3728     if ($element_state->{has_significant}) {
3729     $element_state->{has_sig} = 1;
3730     delete $element_state->{has_significant};
3731     }
3732     $element_state->{phase} = 'in-rb';
3733     }
3734     } elsif ($element_state->{phase} eq 'after-rp2') {
3735     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3736     if ($element_state->{has_significant}) {
3737     $element_state->{has_sig} = 1;
3738     delete $element_state->{has_significant};
3739     }
3740     $element_state->{phase} = 'in-rb';
3741     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3742     $self->{onerror}->(node => $child_el,
3743 wakaba 1.104 level => $self->{level}->{should},
3744     type => 'no significant content before');
3745 wakaba 1.99 $element_state->{phase} = 'after-rt';
3746     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3747     $self->{onerror}->(node => $child_el,
3748 wakaba 1.104 level => $self->{level}->{should},
3749     type => 'no significant content before');
3750 wakaba 1.99 $element_state->{phase} = 'after-rp1';
3751     } else {
3752     $self->{onerror}->(node => $child_el,
3753 wakaba 1.104 type => 'element not allowed:ruby base',
3754     level => $self->{level}->{must});
3755 wakaba 1.99 if ($element_state->{has_significant}) {
3756     $element_state->{has_sig} = 1;
3757     delete $element_state->{has_significant};
3758     }
3759     $element_state->{phase} = 'in-rb';
3760     }
3761     } else {
3762     die "check_child_element: Bad |ruby| phase: $element_state->{phase}";
3763     }
3764     },
3765     check_child_text => sub {
3766     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3767     if ($has_significant) {
3768     if ($element_state->{phase} eq 'before-rb') {
3769     $element_state->{phase} = 'in-rb';
3770     } elsif ($element_state->{phase} eq 'in-rb') {
3771     #
3772     } elsif ($element_state->{phase} eq 'after-rt' or
3773     $element_state->{phase} eq 'after-rp2') {
3774     $element_state->{phase} = 'in-rb';
3775     } elsif ($element_state->{phase} eq 'after-rp1') {
3776     $self->{onerror}->(node => $child_node,
3777 wakaba 1.104 type => 'ps element missing',
3778     text => 'rt',
3779     level => $self->{level}->{must});
3780 wakaba 1.99 $self->{onerror}->(node => $child_node,
3781 wakaba 1.104 type => 'ps element missing',
3782     text => 'rp',
3783     level => $self->{level}->{must});
3784 wakaba 1.99 $element_state->{phase} = 'in-rb';
3785     } elsif ($element_state->{phase} eq 'after-rp-rt') {
3786     $self->{onerror}->(node => $child_node,
3787 wakaba 1.104 type => 'ps element missing',
3788     text => 'rp',
3789     level => $self->{level}->{must});
3790 wakaba 1.99 $element_state->{phase} = 'in-rb';
3791     } else {
3792     die "check_child_text: Bad |ruby| phase: $element_state->{phase}";
3793     }
3794     }
3795     },
3796     check_end => sub {
3797     my ($self, $item, $element_state) = @_;
3798     $self->_remove_minus_elements ($element_state);
3799    
3800     if ($element_state->{phase} eq 'before-rb') {
3801     $self->{onerror}->(node => $item->{node},
3802 wakaba 1.104 level => $self->{level}->{should},
3803 wakaba 1.99 type => 'no significant content');
3804     $self->{onerror}->(node => $item->{node},
3805 wakaba 1.104 type => 'element missing',
3806     text => 'rt',
3807     level => $self->{level}->{must});
3808 wakaba 1.99 } elsif ($element_state->{phase} eq 'in-rb') {
3809     unless ($element_state->{has_significant}) {
3810     $self->{onerror}->(node => $item->{node},
3811 wakaba 1.104 level => $self->{level}->{should},
3812     type => 'no significant content at the end');
3813 wakaba 1.99 }
3814     $self->{onerror}->(node => $item->{node},
3815 wakaba 1.104 type => 'element missing',
3816     text => 'rt',
3817     level => $self->{level}->{must});
3818 wakaba 1.99 } elsif ($element_state->{phase} eq 'after-rt' or
3819     $element_state->{phase} eq 'after-rp2') {
3820     #
3821     } elsif ($element_state->{phase} eq 'after-rp1') {
3822     $self->{onerror}->(node => $item->{node},
3823 wakaba 1.104 type => 'element missing',
3824     text => 'rt',
3825     level => $self->{level}->{must});
3826 wakaba 1.99 $self->{onerror}->(node => $item->{node},
3827 wakaba 1.104 type => 'element missing',
3828     text => 'rp',
3829     level => $self->{level}->{must});
3830 wakaba 1.99 } elsif ($element_state->{phase} eq 'after-rp-rt') {
3831     $self->{onerror}->(node => $item->{node},
3832 wakaba 1.104 type => 'element missing',
3833     text => 'rp',
3834     level => $self->{level}->{must});
3835 wakaba 1.99 } else {
3836     die "check_child_text: Bad |ruby| phase: $element_state->{phase}";
3837     }
3838    
3839     ## NOTE: A modified version of |check_end| of %AnyChecker.
3840     if ($element_state->{has_significant} or $element_state->{has_sig}) {
3841     $item->{real_parent_state}->{has_significant} = 1;
3842     }
3843     },
3844     };
3845    
3846     $Element->{$HTML_NS}->{rt} = {
3847     %HTMLPhrasingContentChecker,
3848     status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC,
3849     check_attrs => $GetHTMLAttrsChecker->({}, {
3850     %HTMLAttrStatus,
3851     %HTMLM12NXHTML2CommonAttrStatus,
3852     lang => FEATURE_HTML5_DEFAULT,
3853     }),
3854     };
3855    
3856     $Element->{$HTML_NS}->{rp} = {
3857     %HTMLTextChecker,
3858     status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC,
3859     check_attrs => $GetHTMLAttrsChecker->({}, {
3860     %HTMLAttrStatus,
3861     %HTMLM12NXHTML2CommonAttrStatus,
3862     lang => FEATURE_HTML5_DEFAULT,
3863     }),
3864 wakaba 1.100 check_start => sub {
3865 wakaba 1.99 my ($self, $item, $element_state) = @_;
3866     $element_state->{text} = '';
3867 wakaba 1.100
3868     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3869     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3870 wakaba 1.99 },
3871     check_child_text => sub {
3872     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3873     if ($has_significant) {
3874     $element_state->{text} .= $child_node->data;
3875     ## NOTE: |<rp> <!---->(</rp>| is allowed.
3876     }
3877     },
3878     check_end => sub {
3879     my ($self, $item, $element_state) = @_;
3880    
3881     my $p_class = ($item->{parent_state} and
3882     $item->{parent_state}->{phase} and
3883     $item->{parent_state}->{phase} eq 'after-rp2')
3884     ? qr/\p{Pe}/ : qr/\p{Ps}/;
3885     if ($element_state->{text} =~ /\A$p_class\z/) {
3886 wakaba 1.132 #=~ /\A[\x09\x0A\x0C\x0D\x20]*${p_class}[\x09\x0A\x0C\x0D\x20]*\z/) {
3887 wakaba 1.99 #
3888     } else {
3889     $self->{onerror}->(node => $item->{node},
3890 wakaba 1.104 type => 'rp:syntax error',
3891     level => $self->{level}->{must});
3892 wakaba 1.99 }
3893    
3894     $HTMLTextChecker{check_end}->(@_);
3895     },
3896     };
3897    
3898 wakaba 1.29 =pod
3899    
3900     ## TODO:
3901    
3902     +
3903     + <p>Partly because of the confusion described above, authors are
3904     + strongly recommended to always mark up all paragraphs with the
3905     + <code>p</code> element, and to not have any <code>ins</code> or
3906     + <code>del</code> elements that cross across any <span
3907     + title="paragraph">implied paragraphs</span>.</p>
3908     +
3909     (An informative note)
3910    
3911     <p><code>ins</code> elements should not cross <span
3912     + title="paragraph">implied paragraph</span> boundaries.</p>
3913     (normative)
3914    
3915     + <p><code>del</code> elements should not cross <span
3916     + title="paragraph">implied paragraph</span> boundaries.</p>
3917     (normative)
3918    
3919     =cut
3920    
3921 wakaba 1.1 $Element->{$HTML_NS}->{ins} = {
3922 wakaba 1.40 %HTMLTransparentChecker,
3923 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3924 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3925 wakaba 1.1 cite => $HTMLURIAttrChecker,
3926     datetime => $HTMLDatetimeAttrChecker,
3927 wakaba 1.49 }, {
3928     %HTMLAttrStatus,
3929     %HTMLM12NCommonAttrStatus,
3930 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3931 wakaba 1.72 datetime => FEATURE_HTML5_FD | FEATURE_M12N10_REC,
3932 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3933 wakaba 1.1 }),
3934 wakaba 1.66 check_start => sub {
3935     my ($self, $item, $element_state) = @_;
3936    
3937     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
3938 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3939     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3940 wakaba 1.66 },
3941 wakaba 1.1 };
3942    
3943     $Element->{$HTML_NS}->{del} = {
3944 wakaba 1.40 %HTMLTransparentChecker,
3945 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3946 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3947 wakaba 1.1 cite => $HTMLURIAttrChecker,
3948     datetime => $HTMLDatetimeAttrChecker,
3949 wakaba 1.49 }, {
3950     %HTMLAttrStatus,
3951     %HTMLM12NCommonAttrStatus,
3952 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3953 wakaba 1.72 datetime => FEATURE_HTML5_FD | FEATURE_M12N10_REC,
3954 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3955 wakaba 1.1 }),
3956 wakaba 1.40 check_end => sub {
3957     my ($self, $item, $element_state) = @_;
3958     if ($element_state->{has_significant}) {
3959     ## NOTE: Significantness flag does not propagate.
3960     } elsif ($item->{transparent}) {
3961     #
3962     } else {
3963     $self->{onerror}->(node => $item->{node},
3964 wakaba 1.104 level => $self->{level}->{should},
3965 wakaba 1.40 type => 'no significant content');
3966     }
3967 wakaba 1.1 },
3968 wakaba 1.66 check_start => sub {
3969     my ($self, $item, $element_state) = @_;
3970    
3971     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
3972 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3973     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3974 wakaba 1.66 },
3975 wakaba 1.1 };
3976    
3977 wakaba 1.35 $Element->{$HTML_NS}->{figure} = {
3978 wakaba 1.72 %HTMLFlowContentChecker,
3979 wakaba 1.48 status => FEATURE_HTML5_FD,
3980 wakaba 1.72 ## NOTE: legend, Flow | Flow, legend?
3981 wakaba 1.41 check_child_element => sub {
3982     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3983     $child_is_transparent, $element_state) = @_;
3984 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
3985     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
3986 wakaba 1.41 $self->{onerror}->(node => $child_el,
3987     type => 'element not allowed:minus',
3988 wakaba 1.104 level => $self->{level}->{must});
3989 wakaba 1.41 $element_state->{has_non_legend} = 1;
3990     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3991     #
3992     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') {
3993     if ($element_state->{has_legend_at_first}) {
3994     $self->{onerror}->(node => $child_el,
3995     type => 'element not allowed:figure legend',
3996 wakaba 1.104 level => $self->{level}->{must});
3997 wakaba 1.41 } elsif ($element_state->{has_legend}) {
3998     $self->{onerror}->(node => $element_state->{has_legend},
3999     type => 'element not allowed:figure legend',
4000 wakaba 1.104 level => $self->{level}->{must});
4001 wakaba 1.41 $element_state->{has_legend} = $child_el;
4002     } elsif ($element_state->{has_non_legend}) {
4003     $element_state->{has_legend} = $child_el;
4004     } else {
4005     $element_state->{has_legend_at_first} = 1;
4006 wakaba 1.35 }
4007 wakaba 1.41 delete $element_state->{has_non_legend};
4008     } else {
4009 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
4010 wakaba 1.43 $element_state->{has_non_legend} = 1 unless $child_is_transparent;
4011 wakaba 1.41 }
4012     },
4013     check_child_text => sub {
4014     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4015     if ($has_significant) {
4016     $element_state->{has_non_legend} = 1;
4017 wakaba 1.35 }
4018 wakaba 1.41 },
4019     check_end => sub {
4020     my ($self, $item, $element_state) = @_;
4021 wakaba 1.35
4022 wakaba 1.41 if ($element_state->{has_legend_at_first}) {
4023     #
4024     } elsif ($element_state->{has_legend}) {
4025     if ($element_state->{has_non_legend}) {
4026     $self->{onerror}->(node => $element_state->{has_legend},
4027 wakaba 1.35 type => 'element not allowed:figure legend',
4028 wakaba 1.104 level => $self->{level}->{must});
4029 wakaba 1.35 }
4030     }
4031 wakaba 1.41
4032 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
4033 wakaba 1.41 ## ISSUE: |<figure><legend>aa</legend></figure>| should be an error?
4034 wakaba 1.35 },
4035     };
4036 wakaba 1.8 ## TODO: Test for <nest/> in <figure/>
4037 wakaba 1.1
4038 wakaba 1.92 my $AttrCheckerNotImplemented = sub {
4039     my ($self, $attr) = @_;
4040 wakaba 1.104 $self->{onerror}->(node => $attr,
4041     type => 'unknown attribute',
4042     level => $self->{level}->{uncertain});
4043 wakaba 1.92 };
4044    
4045 wakaba 1.1 $Element->{$HTML_NS}->{img} = {
4046 wakaba 1.40 %HTMLEmptyChecker,
4047 wakaba 1.98 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4048 wakaba 1.40 check_attrs => sub {
4049     my ($self, $item, $element_state) = @_;
4050 wakaba 1.1 $GetHTMLAttrsChecker->({
4051 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
4052     bottom => 1, middle => 1, top => 1, left => 1, right => 1,
4053     }),
4054 wakaba 1.1 alt => sub { }, ## NOTE: No syntactical requirement
4055 wakaba 1.70 border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4056 wakaba 1.1 src => $HTMLURIAttrChecker,
4057     usemap => $HTMLUsemapAttrChecker,
4058 wakaba 1.70 hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4059 wakaba 1.1 ismap => sub {
4060 wakaba 1.40 my ($self, $attr, $parent_item) = @_;
4061     if (not $self->{flag}->{in_a_href}) {
4062 wakaba 1.15 $self->{onerror}->(node => $attr,
4063 wakaba 1.59 type => 'attribute not allowed:ismap',
4064 wakaba 1.104 level => $self->{level}->{must});
4065 wakaba 1.1 }
4066 wakaba 1.40 $GetHTMLBooleanAttrChecker->('ismap')->($self, $attr, $parent_item);
4067 wakaba 1.1 },
4068 wakaba 1.70 longdesc => $HTMLURIAttrChecker,
4069     ## TODO: HTML4 |name|
4070 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4071 wakaba 1.70 vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4072 wakaba 1.92 width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4073 wakaba 1.49 }, {
4074     %HTMLAttrStatus,
4075 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4076 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
4077 wakaba 1.98 alt => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4078 wakaba 1.49 border => FEATURE_M12N10_REC_DEPRECATED,
4079 wakaba 1.98 height => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4080 wakaba 1.49 hspace => FEATURE_M12N10_REC_DEPRECATED,
4081 wakaba 1.98 ismap => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4082 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4083 wakaba 1.49 longdesc => FEATURE_M12N10_REC,
4084     name => FEATURE_M12N10_REC_DEPRECATED,
4085 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
4086 wakaba 1.98 src => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4087     usemap => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4088 wakaba 1.49 vspace => FEATURE_M12N10_REC_DEPRECATED,
4089 wakaba 1.98 width => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4090 wakaba 1.66 })->($self, $item, $element_state);
4091 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'alt')) {
4092     $self->{onerror}->(node => $item->{node},
4093 wakaba 1.104 type => 'attribute missing',
4094     text => 'alt',
4095     level => $self->{level}->{should});
4096 wakaba 1.114 ## TODO: ...
4097 wakaba 1.1 }
4098 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'src')) {
4099     $self->{onerror}->(node => $item->{node},
4100 wakaba 1.104 type => 'attribute missing',
4101     text => 'src',
4102     level => $self->{level}->{must});
4103 wakaba 1.1 }
4104 wakaba 1.66
4105 wakaba 1.114 ## TODO: external resource check
4106    
4107 wakaba 1.66 $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4108     $element_state->{uri_info}->{lowsrc}->{type}->{embedded} = 1;
4109     $element_state->{uri_info}->{dynsrc}->{type}->{embedded} = 1;
4110     $element_state->{uri_info}->{longdesc}->{type}->{cite} = 1;
4111 wakaba 1.1 },
4112     };
4113    
4114     $Element->{$HTML_NS}->{iframe} = {
4115 wakaba 1.40 %HTMLTextChecker,
4116 wakaba 1.114 status => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4117 wakaba 1.49 ## NOTE: Not part of M12N10 Strict
4118 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4119 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4120 wakaba 1.76 name => $HTMLBrowsingContextNameAttrChecker,
4121 wakaba 1.92 sandbox => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->({
4122     'allow-same-origin' => 1, 'allow-forms' => 1, 'allow-scripts' => 1,
4123     }),
4124     seemless => $GetHTMLBooleanAttrChecker->('seemless'),
4125 wakaba 1.1 src => $HTMLURIAttrChecker,
4126 wakaba 1.92 width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4127 wakaba 1.49 }, {
4128     %HTMLAttrStatus,
4129     %HTMLM12NCommonAttrStatus,
4130     align => FEATURE_XHTML10_REC,
4131 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4132 wakaba 1.49 frameborder => FEATURE_M12N10_REC,
4133 wakaba 1.92 height => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4134 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4135 wakaba 1.49 longdesc => FEATURE_M12N10_REC,
4136     marginheight => FEATURE_M12N10_REC,
4137     marginwidth => FEATURE_M12N10_REC,
4138 wakaba 1.114 #name => FEATURE_HTML5_WD | FEATURE_M12N10_REC_DEPRECATED,
4139     name => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4140     sandbox => FEATURE_HTML5_WD,
4141 wakaba 1.49 scrolling => FEATURE_M12N10_REC,
4142 wakaba 1.114 seemless => FEATURE_HTML5_WD,
4143     src => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4144 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4145 wakaba 1.92 width => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4146 wakaba 1.1 }),
4147 wakaba 1.66 check_start => sub {
4148     my ($self, $item, $element_state) = @_;
4149    
4150     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4151 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4152     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4153 wakaba 1.66 },
4154 wakaba 1.40 };
4155    
4156 wakaba 1.1 $Element->{$HTML_NS}->{embed} = {
4157 wakaba 1.40 %HTMLEmptyChecker,
4158 wakaba 1.98 status => FEATURE_HTML5_WD,
4159 wakaba 1.40 check_attrs => sub {
4160     my ($self, $item, $element_state) = @_;
4161 wakaba 1.1 my $has_src;
4162 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
4163 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
4164     $attr_ns = '' unless defined $attr_ns;
4165     my $attr_ln = $attr->manakai_local_name;
4166     my $checker;
4167 wakaba 1.73
4168     my $status = {
4169     %HTMLAttrStatus,
4170     height => FEATURE_HTML5_DEFAULT,
4171 wakaba 1.98 src => FEATURE_HTML5_WD,
4172     type => FEATURE_HTML5_WD,
4173 wakaba 1.73 width => FEATURE_HTML5_DEFAULT,
4174     }->{$attr_ln};
4175    
4176 wakaba 1.1 if ($attr_ns eq '') {
4177     if ($attr_ln eq 'src') {
4178     $checker = $HTMLURIAttrChecker;
4179     $has_src = 1;
4180     } elsif ($attr_ln eq 'type') {
4181     $checker = $HTMLIMTAttrChecker;
4182 wakaba 1.92 } elsif ($attr_ln eq 'width' or $attr_ln eq 'height') {
4183     $checker = $AttrCheckerNotImplemented; ## TODO: because spec does not define them yet.
4184 wakaba 1.122 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and
4185     $attr_ln !~ /[A-Z]/) {
4186 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
4187     $status = $HTMLDatasetAttrStatus;
4188 wakaba 1.117 } elsif ($attr_ln !~ /^[Xx][Mm][Ll]/ and
4189 wakaba 1.118 $attr_ln !~ /[A-Z]/ and
4190 wakaba 1.117 $attr_ln =~ /\A\p{InXML_NCNameStartChar10}\p{InXMLNCNameChar10}*\z/) {
4191 wakaba 1.1 $checker = $HTMLAttrChecker->{$attr_ln}
4192     || sub { }; ## NOTE: Any local attribute is ok.
4193 wakaba 1.98 $status = FEATURE_HTML5_WD | FEATURE_ALLOWED;
4194 wakaba 1.117 } else {
4195     $checker = $HTMLAttrChecker->{$attr_ln};
4196 wakaba 1.1 }
4197     }
4198     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
4199 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
4200     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
4201     || $AttrStatus->{$attr_ns}->{''};
4202     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
4203 wakaba 1.62
4204 wakaba 1.1 if ($checker) {
4205 wakaba 1.66 $checker->($self, $attr, $item, $element_state);
4206 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
4207 wakaba 1.54 #
4208 wakaba 1.1 } else {
4209 wakaba 1.104 $self->{onerror}->(node => $attr,
4210     type => 'unknown attribute',
4211     level => $self->{level}->{uncertain});
4212 wakaba 1.50 ## ISSUE: No conformance createria for global attributes in the spec
4213     }
4214    
4215 wakaba 1.82 $self->_attr_status_info ($attr, $status);
4216 wakaba 1.1 }
4217    
4218     unless ($has_src) {
4219 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4220 wakaba 1.104 type => 'attribute missing',
4221     text => 'src',
4222 wakaba 1.114 level => $self->{level}->{info});
4223     ## NOTE: <embed> without src="" is allowed since revision 1929.
4224     ## We issues an informational message since <embed> w/o src=""
4225     ## is likely an authoring error.
4226 wakaba 1.1 }
4227 wakaba 1.114
4228     ## TODO: external resource check
4229 wakaba 1.66
4230     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4231 wakaba 1.1 },
4232     };
4233    
4234 wakaba 1.49 ## TODO:
4235     ## {applet} FEATURE_M12N10_REC_DEPRECATED
4236     ## class, id, title, alt, archive, code, codebase, height, object, width name style,hspace,vspace(xhtml10)
4237    
4238 wakaba 1.1 $Element->{$HTML_NS}->{object} = {
4239 wakaba 1.40 %HTMLTransparentChecker,
4240 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4241 wakaba 1.40 check_attrs => sub {
4242     my ($self, $item, $element_state) = @_;
4243 wakaba 1.1 $GetHTMLAttrsChecker->({
4244 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
4245     bottom => 1, middle => 1, top => 1, left => 1, right => 1,
4246     }),
4247     archive => $HTMLSpaceURIsAttrChecker,
4248     ## TODO: Relative to @codebase
4249     border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4250     classid => $HTMLURIAttrChecker,
4251     codebase => $HTMLURIAttrChecker,
4252     codetype => $HTMLIMTAttrChecker,
4253     ## TODO: "RECOMMENDED when |classid| is specified" [HTML4]
4254 wakaba 1.1 data => $HTMLURIAttrChecker,
4255 wakaba 1.70 declare => $GetHTMLBooleanAttrChecker->('declare'),
4256     ## NOTE: "The object MUST be instantiated by a subsequent OBJECT ..."
4257     ## [HTML4] but we don't know how to test this.
4258     hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4259 wakaba 1.76 name => $HTMLBrowsingContextNameAttrChecker,
4260 wakaba 1.70 standby => sub {}, ## NOTE: %Text; in HTML4
4261 wakaba 1.1 type => $HTMLIMTAttrChecker,
4262     usemap => $HTMLUsemapAttrChecker,
4263 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4264 wakaba 1.70 vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4265 wakaba 1.92 width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4266 wakaba 1.49 }, {
4267     %HTMLAttrStatus,
4268 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4269 wakaba 1.49 align => FEATURE_XHTML10_REC,
4270 wakaba 1.82 archive => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4271 wakaba 1.49 border => FEATURE_XHTML10_REC,
4272     classid => FEATURE_M12N10_REC,
4273     codebase => FEATURE_M12N10_REC,
4274     codetype => FEATURE_M12N10_REC,
4275 wakaba 1.82 'content-length' => FEATURE_XHTML2_ED,
4276 wakaba 1.50 data => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4277 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
4278     dataformatas => FEATURE_HTML4_REC_RESERVED,
4279     datasrc => FEATURE_HTML4_REC_RESERVED,
4280 wakaba 1.82 declare => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4281 wakaba 1.50 height => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4282 wakaba 1.49 hspace => FEATURE_XHTML10_REC,
4283 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4284 wakaba 1.76 name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4285 wakaba 1.49 standby => FEATURE_M12N10_REC,
4286 wakaba 1.50 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4287     type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4288     usemap => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4289 wakaba 1.49 vspace => FEATURE_XHTML10_REC,
4290 wakaba 1.50 width => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4291 wakaba 1.66 })->($self, $item, $element_state);
4292 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'data')) {
4293     unless ($item->{node}->has_attribute_ns (undef, 'type')) {
4294     $self->{onerror}->(node => $item->{node},
4295 wakaba 1.104 type => 'attribute missing:data|type',
4296     level => $self->{level}->{must});
4297 wakaba 1.1 }
4298     }
4299 wakaba 1.66
4300     $element_state->{uri_info}->{data}->{type}->{embedded} = 1;
4301     $element_state->{uri_info}->{classid}->{type}->{embedded} = 1;
4302     $element_state->{uri_info}->{codebase}->{type}->{base} = 1;
4303     ## TODO: archive
4304     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4305 wakaba 1.1 },
4306 wakaba 1.72 ## NOTE: param*, transparent (Flow)
4307 wakaba 1.41 check_child_element => sub {
4308     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4309     $child_is_transparent, $element_state) = @_;
4310 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
4311     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
4312 wakaba 1.41 $self->{onerror}->(node => $child_el,
4313     type => 'element not allowed:minus',
4314 wakaba 1.104 level => $self->{level}->{must});
4315 wakaba 1.41 $element_state->{has_non_legend} = 1;
4316     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4317     #
4318     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'param') {
4319     if ($element_state->{has_non_param}) {
4320 wakaba 1.104 $self->{onerror}->(node => $child_el,
4321 wakaba 1.72 type => 'element not allowed:flow',
4322 wakaba 1.104 level => $self->{level}->{must});
4323 wakaba 1.39 }
4324 wakaba 1.41 } else {
4325 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
4326 wakaba 1.41 $element_state->{has_non_param} = 1;
4327 wakaba 1.39 }
4328 wakaba 1.25 },
4329 wakaba 1.41 check_child_text => sub {
4330     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4331     if ($has_significant) {
4332     $element_state->{has_non_param} = 1;
4333     }
4334 wakaba 1.42 },
4335     check_end => sub {
4336     my ($self, $item, $element_state) = @_;
4337     if ($element_state->{has_significant}) {
4338 wakaba 1.46 $item->{real_parent_state}->{has_significant} = 1;
4339 wakaba 1.42 } elsif ($item->{node}->manakai_parent_element) {
4340     ## NOTE: Transparent.
4341     } else {
4342     $self->{onerror}->(node => $item->{node},
4343 wakaba 1.104 level => $self->{level}->{should},
4344 wakaba 1.42 type => 'no significant content');
4345     }
4346     },
4347 wakaba 1.8 ## TODO: Tests for <nest/> in <object/>
4348 wakaba 1.1 };
4349 wakaba 1.41 ## ISSUE: Is |<menu><object data><li>aa</li></object></menu>| conforming?
4350     ## What about |<section><object data><style scoped></style>x</object></section>|?
4351     ## |<section><ins></ins><object data><style scoped></style>x</object></section>|?
4352 wakaba 1.1
4353     $Element->{$HTML_NS}->{param} = {
4354 wakaba 1.40 %HTMLEmptyChecker,
4355 wakaba 1.94 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4356 wakaba 1.40 check_attrs => sub {
4357     my ($self, $item, $element_state) = @_;
4358 wakaba 1.1 $GetHTMLAttrsChecker->({
4359     name => sub { },
4360 wakaba 1.70 type => $HTMLIMTAttrChecker,
4361 wakaba 1.1 value => sub { },
4362 wakaba 1.70 valuetype => $GetHTMLEnumeratedAttrChecker->({
4363     data => 1, ref => 1, object => 1,
4364     }),
4365 wakaba 1.49 }, {
4366     %HTMLAttrStatus,
4367 wakaba 1.121 href => FEATURE_RDFA_PR,
4368 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4369 wakaba 1.94 name => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4370 wakaba 1.82 type => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4371 wakaba 1.94 value => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4372 wakaba 1.82 valuetype => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4373 wakaba 1.66 })->(@_);
4374 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'name')) {
4375     $self->{onerror}->(node => $item->{node},
4376 wakaba 1.104 type => 'attribute missing',
4377     text => 'name',
4378     level => $self->{level}->{must});
4379 wakaba 1.1 }
4380 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'value')) {
4381     $self->{onerror}->(node => $item->{node},
4382 wakaba 1.104 type => 'attribute missing',
4383     text => 'value',
4384     level => $self->{level}->{must});
4385 wakaba 1.1 }
4386     },
4387     };
4388    
4389     $Element->{$HTML_NS}->{video} = {
4390 wakaba 1.40 %HTMLTransparentChecker,
4391 wakaba 1.48 status => FEATURE_HTML5_LC,
4392 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4393 wakaba 1.1 src => $HTMLURIAttrChecker,
4394     ## TODO: start, loopstart, loopend, end
4395     ## ISSUE: they MUST be "value time offset"s. Value?
4396 wakaba 1.11 ## ISSUE: playcount has no conformance creteria
4397 wakaba 1.1 autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
4398     controls => $GetHTMLBooleanAttrChecker->('controls'),
4399 wakaba 1.59 poster => $HTMLURIAttrChecker,
4400 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4401     width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4402 wakaba 1.50 }, {
4403     %HTMLAttrStatus,
4404     autoplay => FEATURE_HTML5_LC,
4405     controls => FEATURE_HTML5_LC,
4406     end => FEATURE_HTML5_LC,
4407     height => FEATURE_HTML5_LC,
4408     loopend => FEATURE_HTML5_LC,
4409     loopstart => FEATURE_HTML5_LC,
4410     playcount => FEATURE_HTML5_LC,
4411     poster => FEATURE_HTML5_LC,
4412     src => FEATURE_HTML5_LC,
4413     start => FEATURE_HTML5_LC,
4414     width => FEATURE_HTML5_LC,
4415 wakaba 1.1 }),
4416 wakaba 1.42 check_start => sub {
4417     my ($self, $item, $element_state) = @_;
4418     $element_state->{allow_source}
4419     = not $item->{node}->has_attribute_ns (undef, 'src');
4420     $element_state->{has_source} ||= $element_state->{allow_source} * -1;
4421     ## NOTE: It might be set true by |check_element|.
4422 wakaba 1.66
4423     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4424     $element_state->{uri_info}->{poster}->{type}->{embedded} = 1;
4425 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4426     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4427 wakaba 1.42 },
4428     check_child_element => sub {
4429     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4430     $child_is_transparent, $element_state) = @_;
4431 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
4432     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
4433 wakaba 1.42 $self->{onerror}->(node => $child_el,
4434     type => 'element not allowed:minus',
4435 wakaba 1.104 level => $self->{level}->{must});
4436 wakaba 1.42 delete $element_state->{allow_source};
4437     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4438     #
4439     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'source') {
4440 wakaba 1.45 unless ($element_state->{allow_source}) {
4441 wakaba 1.104 $self->{onerror}->(node => $child_el,
4442 wakaba 1.72 type => 'element not allowed:flow',
4443 wakaba 1.104 level => $self->{level}->{must});
4444 wakaba 1.42 }
4445 wakaba 1.45 $element_state->{has_source} = 1;
4446 wakaba 1.1 } else {
4447 wakaba 1.42 delete $element_state->{allow_source};
4448 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
4449 wakaba 1.42 }
4450     },
4451     check_child_text => sub {
4452     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4453     if ($has_significant) {
4454     delete $element_state->{allow_source};
4455     }
4456 wakaba 1.72 $HTMLFlowContentChecker{check_child_text}->(@_);
4457 wakaba 1.42 },
4458     check_end => sub {
4459     my ($self, $item, $element_state) = @_;
4460     if ($element_state->{has_source} == -1) {
4461     $self->{onerror}->(node => $item->{node},
4462 wakaba 1.104 type => 'child element missing',
4463     text => 'source',
4464     level => $self->{level}->{must});
4465 wakaba 1.1 }
4466 wakaba 1.42
4467     $Element->{$HTML_NS}->{object}->{check_end}->(@_);
4468 wakaba 1.1 },
4469     };
4470    
4471     $Element->{$HTML_NS}->{audio} = {
4472 wakaba 1.40 %{$Element->{$HTML_NS}->{video}},
4473 wakaba 1.48 status => FEATURE_HTML5_LC,
4474 wakaba 1.42 check_attrs => $GetHTMLAttrsChecker->({
4475     src => $HTMLURIAttrChecker,
4476     ## TODO: start, loopstart, loopend, end
4477     ## ISSUE: they MUST be "value time offset"s. Value?
4478     ## ISSUE: playcount has no conformance creteria
4479     autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
4480     controls => $GetHTMLBooleanAttrChecker->('controls'),
4481 wakaba 1.50 }, {
4482     %HTMLAttrStatus,
4483     autoplay => FEATURE_HTML5_LC,
4484     controls => FEATURE_HTML5_LC,
4485     end => FEATURE_HTML5_LC,
4486     loopend => FEATURE_HTML5_LC,
4487     loopstart => FEATURE_HTML5_LC,
4488     playcount => FEATURE_HTML5_LC,
4489     src => FEATURE_HTML5_LC,
4490     start => FEATURE_HTML5_LC,
4491 wakaba 1.42 }),
4492 wakaba 1.1 };
4493    
4494     $Element->{$HTML_NS}->{source} = {
4495 wakaba 1.40 %HTMLEmptyChecker,
4496 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
4497 wakaba 1.40 check_attrs => sub {
4498     my ($self, $item, $element_state) = @_;
4499 wakaba 1.1 $GetHTMLAttrsChecker->({
4500 wakaba 1.90 media => $HTMLMQAttrChecker,
4501     pixelratio => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
4502     src => $HTMLURIAttrChecker, ## ISSUE: Negative or zero pixelratio=""
4503 wakaba 1.1 type => $HTMLIMTAttrChecker,
4504 wakaba 1.50 }, {
4505     %HTMLAttrStatus,
4506     media => FEATURE_HTML5_DEFAULT,
4507 wakaba 1.90 pixelratio => FEATURE_HTML5_DEFAULT,
4508 wakaba 1.50 src => FEATURE_HTML5_DEFAULT,
4509     type => FEATURE_HTML5_DEFAULT,
4510 wakaba 1.66 })->(@_);
4511 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'src')) {
4512     $self->{onerror}->(node => $item->{node},
4513 wakaba 1.104 type => 'attribute missing',
4514     text => 'src',
4515     level => $self->{level}->{must});
4516 wakaba 1.1 }
4517 wakaba 1.66
4518     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4519 wakaba 1.1 },
4520     };
4521    
4522     $Element->{$HTML_NS}->{canvas} = {
4523 wakaba 1.40 %HTMLTransparentChecker,
4524 wakaba 1.89 status => FEATURE_HTML5_COMPLETE,
4525 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4526 wakaba 1.1 height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4527     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4528 wakaba 1.50 }, {
4529     %HTMLAttrStatus,
4530 wakaba 1.89 height => FEATURE_HTML5_COMPLETE,
4531     width => FEATURE_HTML5_COMPLETE,
4532 wakaba 1.1 }),
4533     };
4534    
4535     $Element->{$HTML_NS}->{map} = {
4536 wakaba 1.72 %HTMLFlowContentChecker,
4537 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4538 wakaba 1.40 check_attrs => sub {
4539     my ($self, $item, $element_state) = @_;
4540 wakaba 1.100 my $has_name;
4541 wakaba 1.4 $GetHTMLAttrsChecker->({
4542 wakaba 1.100 name => sub {
4543     my ($self, $attr) = @_;
4544     my $value = $attr->value;
4545     if (length $value) {
4546     ## NOTE: Duplication is not non-conforming.
4547     ## NOTE: Space characters are not non-conforming.
4548     #
4549     } else {
4550     $self->{onerror}->(node => $attr,
4551     type => 'empty attribute value',
4552 wakaba 1.104 level => $self->{level}->{must});
4553 wakaba 1.100 }
4554 wakaba 1.4 $self->{map}->{$value} ||= $attr;
4555 wakaba 1.100 $has_name = [$value, $attr];
4556 wakaba 1.4 },
4557 wakaba 1.49 }, {
4558     %HTMLAttrStatus,
4559 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4560     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4561     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4562     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4563 wakaba 1.100 #name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
4564     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4565 wakaba 1.50 onclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4566     ondblclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4567     onmousedown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4568     onmouseup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4569     onmouseover => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4570     onmousemove => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4571     onmouseout => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4572     onkeypress => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4573     onkeydown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4574     onkeyup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4575     title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4576 wakaba 1.66 })->(@_);
4577 wakaba 1.100
4578 wakaba 1.135 if ($has_name) {
4579 wakaba 1.145 my $id = $item->{node}->get_attribute_ns (undef, 'id');
4580 wakaba 1.135 if (defined $id and $has_name->[0] ne $id) {
4581     $self->{onerror}->(node => $item->{node}->get_attribute_node ('id'),
4582 wakaba 1.104 type => 'id ne name',
4583     level => $self->{level}->{must});
4584 wakaba 1.100 }
4585 wakaba 1.135 } else {
4586 wakaba 1.100 $self->{onerror}->(node => $item->{node},
4587 wakaba 1.104 type => 'attribute missing',
4588     text => 'name',
4589     level => $self->{level}->{must});
4590 wakaba 1.100 }
4591 wakaba 1.4 },
4592 wakaba 1.59 check_start => sub {
4593     my ($self, $item, $element_state) = @_;
4594     $element_state->{in_map_original} = $self->{flag}->{in_map};
4595 wakaba 1.137 $self->{flag}->{in_map} = [@{$self->{flag}->{in_map} or []}, {}];
4596     ## NOTE: |{in_map}| is a reference to the array which contains
4597     ## hash references. Hashes are corresponding to the opening
4598     ## |map| elements and each of them contains the key-value
4599     ## pairs corresponding to the absolute URLs for the processed
4600     ## |area| elements in the |map| element corresponding to the
4601     ## hash. The key represents the resource (## TODO: use
4602     ## absolute URL), while the value represents whether there is
4603     ## an |area| element whose |alt| attribute is specified to a
4604     ## non-empty value. If there IS such an |area| element for
4605     ## the resource specified by the key, then the value is set to
4606     ## zero (|0|). Otherwise, if there is no such an |area|
4607     ## element but there is any |area| element with the empty
4608     ## |alt=""| attribute, then the value contains an array
4609     ## reference that contains all of such |area| elements.
4610 wakaba 1.79
4611     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4612     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4613 wakaba 1.59 },
4614     check_end => sub {
4615     my ($self, $item, $element_state) = @_;
4616 wakaba 1.137
4617     for (keys %{$self->{flag}->{in_map}->[-1]}) {
4618     my $nodes = $self->{flag}->{in_map}->[-1]->{$_};
4619     next unless $nodes;
4620     for (@$nodes) {
4621     $self->{onerror}->(type => 'empty area alt',
4622     node => $_,
4623     level => $self->{level}->{html5_no_may});
4624     }
4625     }
4626    
4627     $self->{flag}->{in_map} = $element_state->{in_map_original};
4628    
4629 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
4630 wakaba 1.59 },
4631 wakaba 1.1 };
4632    
4633     $Element->{$HTML_NS}->{area} = {
4634 wakaba 1.40 %HTMLEmptyChecker,
4635 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4636 wakaba 1.40 check_attrs => sub {
4637     my ($self, $item, $element_state) = @_;
4638 wakaba 1.1 my %attr;
4639     my $coords;
4640 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
4641 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
4642     $attr_ns = '' unless defined $attr_ns;
4643     my $attr_ln = $attr->manakai_local_name;
4644     my $checker;
4645 wakaba 1.73 my $status;
4646 wakaba 1.1 if ($attr_ns eq '') {
4647 wakaba 1.73 $status = {
4648     %HTMLAttrStatus,
4649     %HTMLM12NCommonAttrStatus,
4650     accesskey => FEATURE_M12N10_REC,
4651     alt => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4652     coords => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4653 wakaba 1.121 href => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_CR | FEATURE_M12N10_REC,
4654 wakaba 1.73 hreflang => FEATURE_HTML5_DEFAULT,
4655     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4656     media => FEATURE_HTML5_DEFAULT,
4657     nohref => FEATURE_M12N10_REC,
4658     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4659     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4660     ping => FEATURE_HTML5_DEFAULT,
4661 wakaba 1.120 rel => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_CR,
4662 wakaba 1.73 shape => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4663     tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4664     target => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4665     type => FEATURE_HTML5_DEFAULT,
4666     }->{$attr_ln};
4667    
4668 wakaba 1.1 $checker = {
4669 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4670 wakaba 1.1 alt => sub { },
4671     ## NOTE: |alt| value has no conformance creteria.
4672     shape => $GetHTMLEnumeratedAttrChecker->({
4673     circ => -1, circle => 1,
4674     default => 1,
4675     poly => 1, polygon => -1,
4676     rect => 1, rectangle => -1,
4677     }),
4678     coords => sub {
4679     my ($self, $attr) = @_;
4680     my $value = $attr->value;
4681     if ($value =~ /\A-?[0-9]+(?>,-?[0-9]+)*\z/) {
4682     $coords = [split /,/, $value];
4683     } else {
4684     $self->{onerror}->(node => $attr,
4685 wakaba 1.104 type => 'coords:syntax error',
4686     level => $self->{level}->{must});
4687 wakaba 1.1 }
4688     },
4689 wakaba 1.70 nohref => $GetHTMLBooleanAttrChecker->('nohref'),
4690     target => $HTMLTargetAttrChecker,
4691 wakaba 1.1 href => $HTMLURIAttrChecker,
4692     ping => $HTMLSpaceURIsAttrChecker,
4693 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) },
4694 wakaba 1.1 media => $HTMLMQAttrChecker,
4695     hreflang => $HTMLLanguageTagAttrChecker,
4696     type => $HTMLIMTAttrChecker,
4697     }->{$attr_ln};
4698     if ($checker) {
4699     $attr{$attr_ln} = $attr;
4700 wakaba 1.122 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and
4701     $attr_ln !~ /[A-Z]/) {
4702 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
4703     $status = $HTMLDatasetAttrStatus;
4704 wakaba 1.1 } else {
4705     $checker = $HTMLAttrChecker->{$attr_ln};
4706     }
4707     }
4708     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
4709 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
4710     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
4711     || $AttrStatus->{$attr_ns}->{''};
4712     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
4713 wakaba 1.62
4714 wakaba 1.1 if ($checker) {
4715 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
4716 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
4717 wakaba 1.54 #
4718 wakaba 1.1 } else {
4719 wakaba 1.104 $self->{onerror}->(node => $attr,
4720     type => 'unknown attribute',
4721     level => $self->{level}->{uncertain});
4722 wakaba 1.1 ## ISSUE: No comformance createria for unknown attributes in the spec
4723     }
4724 wakaba 1.49
4725 wakaba 1.82 $self->_attr_status_info ($attr, $status);
4726 wakaba 1.1 }
4727    
4728     if (defined $attr{href}) {
4729 wakaba 1.4 $self->{has_hyperlink_element} = 1;
4730 wakaba 1.137 if (defined $attr{alt}) {
4731     my $url = $attr{href}->value; ## TODO: resolve
4732     if (length $attr{alt}->value) {
4733     for (@{$self->{flag}->{in_map} or []}) {
4734     $_->{$url} = 0;
4735     }
4736     } else {
4737     ## NOTE: Empty |alt=""|. If there is another |area| element
4738     ## with the same |href=""| and that |area| elemnet's
4739     ## |alt=""| attribute is not an empty string, then this
4740     ## is conforming.
4741     for (@{$self->{flag}->{in_map} or []}) {
4742     push @{$_->{$url} ||= []}, $attr{alt}
4743     unless exists $_->{$url} and not $_->{$url};
4744     }
4745     }
4746     } else {
4747 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4748 wakaba 1.104 type => 'attribute missing',
4749     text => 'alt',
4750     level => $self->{level}->{must});
4751 wakaba 1.1 }
4752     } else {
4753     for (qw/target ping rel media hreflang type alt/) {
4754     if (defined $attr{$_}) {
4755     $self->{onerror}->(node => $attr{$_},
4756 wakaba 1.104 type => 'attribute not allowed',
4757     level => $self->{level}->{must});
4758 wakaba 1.1 }
4759     }
4760     }
4761    
4762     my $shape = 'rectangle';
4763     if (defined $attr{shape}) {
4764     $shape = {
4765     circ => 'circle', circle => 'circle',
4766     default => 'default',
4767     poly => 'polygon', polygon => 'polygon',
4768     rect => 'rectangle', rectangle => 'rectangle',
4769     }->{lc $attr{shape}->value} || 'rectangle';
4770     ## TODO: ASCII lowercase?
4771     }
4772    
4773     if ($shape eq 'circle') {
4774     if (defined $attr{coords}) {
4775     if (defined $coords) {
4776     if (@$coords == 3) {
4777     if ($coords->[2] < 0) {
4778     $self->{onerror}->(node => $attr{coords},
4779 wakaba 1.104 type => 'coords:out of range',
4780     index => 2,
4781     value => $coords->[2],
4782     level => $self->{level}->{must});
4783 wakaba 1.1 }
4784     } else {
4785     $self->{onerror}->(node => $attr{coords},
4786 wakaba 1.104 type => 'coords:number not 3',
4787     text => 0+@$coords,
4788     level => $self->{level}->{must});
4789 wakaba 1.1 }
4790     } else {
4791     ## NOTE: A syntax error has been reported.
4792     }
4793     } else {
4794 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4795 wakaba 1.104 type => 'attribute missing',
4796     text => 'coords',
4797     level => $self->{level}->{must});
4798 wakaba 1.1 }
4799     } elsif ($shape eq 'default') {
4800     if (defined $attr{coords}) {
4801     $self->{onerror}->(node => $attr{coords},
4802 wakaba 1.104 type => 'attribute not allowed',
4803     level => $self->{level}->{must});
4804 wakaba 1.1 }
4805     } elsif ($shape eq 'polygon') {
4806     if (defined $attr{coords}) {
4807     if (defined $coords) {
4808     if (@$coords >= 6) {
4809     unless (@$coords % 2 == 0) {
4810     $self->{onerror}->(node => $attr{coords},
4811 wakaba 1.104 type => 'coords:number not even',
4812     text => 0+@$coords,
4813     level => $self->{level}->{must});
4814 wakaba 1.1 }
4815     } else {
4816     $self->{onerror}->(node => $attr{coords},
4817 wakaba 1.104 type => 'coords:number lt 6',
4818     text => 0+@$coords,
4819     level => $self->{level}->{must});
4820 wakaba 1.1 }
4821     } else {
4822     ## NOTE: A syntax error has been reported.
4823     }
4824     } else {
4825 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4826 wakaba 1.104 type => 'attribute missing',
4827     text => 'coords',
4828     level => $self->{level}->{must});
4829 wakaba 1.1 }
4830     } elsif ($shape eq 'rectangle') {
4831     if (defined $attr{coords}) {
4832     if (defined $coords) {
4833     if (@$coords == 4) {
4834     unless ($coords->[0] < $coords->[2]) {
4835     $self->{onerror}->(node => $attr{coords},
4836 wakaba 1.104 type => 'coords:out of range',
4837     index => 0,
4838     value => $coords->[0],
4839     level => $self->{level}->{must});
4840 wakaba 1.1 }
4841     unless ($coords->[1] < $coords->[3]) {
4842     $self->{onerror}->(node => $attr{coords},
4843 wakaba 1.104 type => 'coords:out of range',
4844     index => 1,
4845     value => $coords->[1],
4846     level => $self->{level}->{must});
4847 wakaba 1.1 }
4848     } else {
4849     $self->{onerror}->(node => $attr{coords},
4850 wakaba 1.104 type => 'coords:number not 4',
4851     text => 0+@$coords,
4852     level => $self->{level}->{must});
4853 wakaba 1.1 }
4854     } else {
4855     ## NOTE: A syntax error has been reported.
4856     }
4857     } else {
4858 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4859 wakaba 1.104 type => 'attribute missing',
4860     text => 'coords',
4861     level => $self->{level}->{must});
4862 wakaba 1.1 }
4863     }
4864 wakaba 1.66
4865     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
4866 wakaba 1.1 },
4867 wakaba 1.59 check_start => sub {
4868     my ($self, $item, $element_state) = @_;
4869     unless ($self->{flag}->{in_map} or
4870     not $item->{node}->manakai_parent_element) {
4871     $self->{onerror}->(node => $item->{node},
4872     type => 'element not allowed:area',
4873 wakaba 1.104 level => $self->{level}->{must});
4874 wakaba 1.59 }
4875 wakaba 1.79
4876     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4877     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4878 wakaba 1.59 },
4879 wakaba 1.1 };
4880    
4881     $Element->{$HTML_NS}->{table} = {
4882 wakaba 1.40 %HTMLChecker,
4883 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4884 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4885 wakaba 1.86 cellpadding => $HTMLLengthAttrChecker,
4886     cellspacing => $HTMLLengthAttrChecker,
4887 wakaba 1.69 frame => $GetHTMLEnumeratedAttrChecker->({
4888     void => 1, above => 1, below => 1, hsides => 1, vsides => 1,
4889     lhs => 1, rhs => 1, box => 1, border => 1,
4890     }),
4891     rules => $GetHTMLEnumeratedAttrChecker->({
4892     none => 1, groups => 1, rows => 1, cols => 1, all => 1,
4893     }),
4894     summary => sub {}, ## NOTE: %Text; in HTML4.
4895     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## %Pixels;
4896     }, {
4897 wakaba 1.49 %HTMLAttrStatus,
4898 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4899 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
4900     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
4901     border => FEATURE_M12N10_REC,
4902     cellpadding => FEATURE_M12N10_REC,
4903     cellspacing => FEATURE_M12N10_REC,
4904 wakaba 1.61 cols => FEATURE_RFC1942,
4905 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
4906     dataformatas => FEATURE_HTML4_REC_RESERVED,
4907     datapagesize => FEATURE_M12N10_REC,
4908     datasrc => FEATURE_HTML4_REC_RESERVED,
4909     frame => FEATURE_M12N10_REC,
4910 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4911 wakaba 1.49 rules => FEATURE_M12N10_REC,
4912     summary => FEATURE_M12N10_REC,
4913     width => FEATURE_M12N10_REC,
4914     }),
4915 wakaba 1.40 check_start => sub {
4916     my ($self, $item, $element_state) = @_;
4917     $element_state->{phase} = 'before caption';
4918 wakaba 1.66
4919     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4920 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4921     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4922 wakaba 1.40 },
4923     check_child_element => sub {
4924     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4925     $child_is_transparent, $element_state) = @_;
4926 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
4927     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
4928 wakaba 1.40 $self->{onerror}->(node => $child_el,
4929     type => 'element not allowed:minus',
4930 wakaba 1.104 level => $self->{level}->{must});
4931 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4932     #
4933     } elsif ($element_state->{phase} eq 'in tbodys') {
4934     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4935     #$element_state->{phase} = 'in tbodys';
4936     } elsif (not $element_state->{has_tfoot} and
4937     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4938     $element_state->{phase} = 'after tfoot';
4939     $element_state->{has_tfoot} = 1;
4940     } else {
4941 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4942     level => $self->{level}->{must});
4943 wakaba 1.40 }
4944     } elsif ($element_state->{phase} eq 'in trs') {
4945     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4946     #$element_state->{phase} = 'in trs';
4947     } elsif (not $element_state->{has_tfoot} and
4948     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4949     $element_state->{phase} = 'after tfoot';
4950     $element_state->{has_tfoot} = 1;
4951     } else {
4952 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4953     level => $self->{level}->{must});
4954 wakaba 1.40 }
4955     } elsif ($element_state->{phase} eq 'after thead') {
4956     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4957     $element_state->{phase} = 'in tbodys';
4958     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4959     $element_state->{phase} = 'in trs';
4960     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4961     $element_state->{phase} = 'in tbodys';
4962     $element_state->{has_tfoot} = 1;
4963     } else {
4964 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4965     level => $self->{level}->{must});
4966 wakaba 1.40 }
4967     } elsif ($element_state->{phase} eq 'in colgroup') {
4968     if ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
4969     $element_state->{phase} = 'in colgroup';
4970     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
4971     $element_state->{phase} = 'after thead';
4972     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4973     $element_state->{phase} = 'in tbodys';
4974     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4975     $element_state->{phase} = 'in trs';
4976     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4977     $element_state->{phase} = 'in tbodys';
4978     $element_state->{has_tfoot} = 1;
4979     } else {
4980 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4981     level => $self->{level}->{must});
4982 wakaba 1.40 }
4983     } elsif ($element_state->{phase} eq 'before caption') {
4984     if ($child_nsuri eq $HTML_NS and $child_ln eq 'caption') {
4985     $element_state->{phase} = 'in colgroup';
4986     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
4987     $element_state->{phase} = 'in colgroup';
4988     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
4989     $element_state->{phase} = 'after thead';
4990     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4991     $element_state->{phase} = 'in tbodys';
4992     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4993     $element_state->{phase} = 'in trs';
4994     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4995     $element_state->{phase} = 'in tbodys';
4996     $element_state->{has_tfoot} = 1;
4997     } else {
4998 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4999     level => $self->{level}->{must});
5000 wakaba 1.40 }
5001     } elsif ($element_state->{phase} eq 'after tfoot') {
5002 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5003     level => $self->{level}->{must});
5004 wakaba 1.40 } else {
5005     die "check_child_element: Bad |table| phase: $element_state->{phase}";
5006     }
5007     },
5008     check_child_text => sub {
5009     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5010     if ($has_significant) {
5011 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
5012     level => $self->{level}->{must});
5013 wakaba 1.1 }
5014 wakaba 1.40 },
5015     check_end => sub {
5016     my ($self, $item, $element_state) = @_;
5017 wakaba 1.1
5018     ## Table model errors
5019     require Whatpm::HTMLTable;
5020 wakaba 1.87 my $table = Whatpm::HTMLTable->form_table ($item->{node}, sub {
5021 wakaba 1.104 $self->{onerror}->(@_);
5022     }, $self->{level});
5023 wakaba 1.87 Whatpm::HTMLTable->assign_header
5024 wakaba 1.104 ($table, $self->{onerror}, $self->{level});
5025 wakaba 1.87 push @{$self->{return}->{table}}, $table;
5026 wakaba 1.1
5027 wakaba 1.40 $HTMLChecker{check_end}->(@_);
5028 wakaba 1.1 },
5029     };
5030    
5031     $Element->{$HTML_NS}->{caption} = {
5032 wakaba 1.40 %HTMLPhrasingContentChecker,
5033 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5034 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
5035     align => $GetHTMLEnumeratedAttrChecker->({
5036     top => 1, bottom => 1, left => 1, right => 1,
5037     }),
5038     }, {
5039 wakaba 1.49 %HTMLAttrStatus,
5040 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5041 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
5042 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5043 wakaba 1.49 }),
5044 wakaba 1.1 };
5045    
5046 wakaba 1.69 my %cellalign = (
5047     ## HTML4 %cellhalign;
5048 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
5049     left => 1, center => 1, right => 1, justify => 1, char => 1,
5050     }),
5051     char => sub {
5052     my ($self, $attr) = @_;
5053 wakaba 1.69
5054 wakaba 1.70 ## NOTE: "character" or |%Character;| in HTML4.
5055    
5056     my $value = $attr->value;
5057     if (length $value != 1) {
5058     $self->{onerror}->(node => $attr, type => 'char:syntax error',
5059 wakaba 1.105 level => $self->{level}->{html4_fact});
5060 wakaba 1.70 }
5061     },
5062 wakaba 1.86 charoff => $HTMLLengthAttrChecker,
5063    
5064 wakaba 1.69 ## HTML4 %cellvalign;
5065 wakaba 1.70 valign => $GetHTMLEnumeratedAttrChecker->({
5066     top => 1, middle => 1, bottom => 1, baseline => 1,
5067     }),
5068 wakaba 1.69 );
5069    
5070 wakaba 1.1 $Element->{$HTML_NS}->{colgroup} = {
5071 wakaba 1.40 %HTMLEmptyChecker,
5072 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5073 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5074 wakaba 1.69 %cellalign,
5075 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5076     ## NOTE: Defined only if "the |colgroup| element contains no |col| elements"
5077     ## TODO: "attribute not supported" if |col|.
5078     ## ISSUE: MUST NOT if any |col|?
5079     ## ISSUE: MUST NOT for |<colgroup span="1"><any><col/></any></colgroup>| (though non-conforming)?
5080 wakaba 1.49 }, {
5081     %HTMLAttrStatus,
5082 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5083 wakaba 1.49 align => FEATURE_M12N10_REC,
5084     char => FEATURE_M12N10_REC,
5085     charoff => FEATURE_M12N10_REC,
5086 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5087 wakaba 1.82 span => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5088 wakaba 1.49 valign => FEATURE_M12N10_REC,
5089     width => FEATURE_M12N10_REC,
5090 wakaba 1.1 }),
5091 wakaba 1.40 check_child_element => sub {
5092     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5093     $child_is_transparent, $element_state) = @_;
5094 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
5095     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
5096 wakaba 1.40 $self->{onerror}->(node => $child_el,
5097     type => 'element not allowed:minus',
5098 wakaba 1.104 level => $self->{level}->{must});
5099 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5100     #
5101     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'col') {
5102     #
5103     } else {
5104 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5105     level => $self->{level}->{must});
5106 wakaba 1.40 }
5107     },
5108     check_child_text => sub {
5109     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5110     if ($has_significant) {
5111 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
5112     level => $self->{level}->{must});
5113 wakaba 1.1 }
5114     },
5115     };
5116    
5117     $Element->{$HTML_NS}->{col} = {
5118 wakaba 1.40 %HTMLEmptyChecker,
5119 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5120 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5121 wakaba 1.69 %cellalign,
5122 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5123 wakaba 1.49 }, {
5124     %HTMLAttrStatus,
5125 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5126 wakaba 1.49 align => FEATURE_M12N10_REC,
5127     char => FEATURE_M12N10_REC,
5128     charoff => FEATURE_M12N10_REC,
5129 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5130 wakaba 1.82 span => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5131 wakaba 1.49 valign => FEATURE_M12N10_REC,
5132     width => FEATURE_M12N10_REC,
5133 wakaba 1.1 }),
5134     };
5135    
5136     $Element->{$HTML_NS}->{tbody} = {
5137 wakaba 1.40 %HTMLChecker,
5138 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5139 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
5140     %cellalign,
5141     }, {
5142 wakaba 1.49 %HTMLAttrStatus,
5143 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5144 wakaba 1.49 align => FEATURE_M12N10_REC,
5145     char => FEATURE_M12N10_REC,
5146     charoff => FEATURE_M12N10_REC,
5147 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5148 wakaba 1.49 valign => FEATURE_M12N10_REC,
5149     }),
5150 wakaba 1.40 check_child_element => sub {
5151     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5152     $child_is_transparent, $element_state) = @_;
5153 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
5154     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
5155 wakaba 1.40 $self->{onerror}->(node => $child_el,
5156     type => 'element not allowed:minus',
5157 wakaba 1.104 level => $self->{level}->{must});
5158 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5159     #
5160     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
5161 wakaba 1.84 #
5162 wakaba 1.40 } else {
5163 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5164     level => $self->{level}->{must});
5165 wakaba 1.40 }
5166     },
5167     check_child_text => sub {
5168     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5169     if ($has_significant) {
5170 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
5171     level => $self->{level}->{must});
5172 wakaba 1.1 }
5173 wakaba 1.40 },
5174 wakaba 1.1 };
5175    
5176     $Element->{$HTML_NS}->{thead} = {
5177 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
5178 wakaba 1.1 };
5179    
5180     $Element->{$HTML_NS}->{tfoot} = {
5181 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
5182 wakaba 1.1 };
5183    
5184     $Element->{$HTML_NS}->{tr} = {
5185 wakaba 1.40 %HTMLChecker,
5186 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5187 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
5188     %cellalign,
5189     bgcolor => $HTMLColorAttrChecker,
5190     }, {
5191 wakaba 1.49 %HTMLAttrStatus,
5192 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5193 wakaba 1.49 align => FEATURE_M12N10_REC,
5194     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
5195     char => FEATURE_M12N10_REC,
5196     charoff => FEATURE_M12N10_REC,
5197 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5198 wakaba 1.49 valign => FEATURE_M12N10_REC,
5199     }),
5200 wakaba 1.40 check_child_element => sub {
5201     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5202     $child_is_transparent, $element_state) = @_;
5203 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
5204     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
5205 wakaba 1.40 $self->{onerror}->(node => $child_el,
5206     type => 'element not allowed:minus',
5207 wakaba 1.104 level => $self->{level}->{must});
5208 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5209     #
5210     } elsif ($child_nsuri eq $HTML_NS and
5211     ($child_ln eq 'td' or $child_ln eq 'th')) {
5212 wakaba 1.84 #
5213 wakaba 1.40 } else {
5214 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5215     level => $self->{level}->{must});
5216 wakaba 1.40 }
5217     },
5218     check_child_text => sub {
5219     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5220     if ($has_significant) {
5221 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
5222     level => $self->{level}->{must});
5223 wakaba 1.1 }
5224     },
5225     };
5226    
5227     $Element->{$HTML_NS}->{td} = {
5228 wakaba 1.72 %HTMLFlowContentChecker,
5229 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5230 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5231 wakaba 1.69 %cellalign,
5232     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
5233     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
5234     bgcolor => $HTMLColorAttrChecker,
5235 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5236 wakaba 1.87 headers => sub {
5237     ## NOTE: Will be checked by Whatpm::HTMLTable->assign_header.
5238     ## Though that method does not check the |headers| attribute of a
5239     ## |td| element if the element does not form a table, in that case
5240     ## the |td| element is non-conforming anyway.
5241     },
5242 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
5243 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5244 wakaba 1.69 scope => $GetHTMLEnumeratedAttrChecker
5245     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
5246 wakaba 1.49 }, {
5247     %HTMLAttrStatus,
5248 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5249     abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5250 wakaba 1.49 align => FEATURE_M12N10_REC,
5251 wakaba 1.82 axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5252 wakaba 1.49 bgcolor => FEATURE_M12N10_REC_DEPRECATED,
5253     char => FEATURE_M12N10_REC,
5254     charoff => FEATURE_M12N10_REC,
5255 wakaba 1.82 colspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5256 wakaba 1.87 headers => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5257 wakaba 1.49 height => FEATURE_M12N10_REC_DEPRECATED,
5258 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5259 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
5260 wakaba 1.82 rowspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5261     scope => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5262 wakaba 1.49 valign => FEATURE_M12N10_REC,
5263     width => FEATURE_M12N10_REC_DEPRECATED,
5264 wakaba 1.1 }),
5265     };
5266    
5267     $Element->{$HTML_NS}->{th} = {
5268 wakaba 1.40 %HTMLPhrasingContentChecker,
5269 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5270 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5271 wakaba 1.69 %cellalign,
5272     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
5273     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
5274     bgcolor => $HTMLColorAttrChecker,
5275 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5276 wakaba 1.87 ## TODO: HTML4(?) |headers|
5277 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
5278 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5279     scope => $GetHTMLEnumeratedAttrChecker
5280     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
5281 wakaba 1.49 }, {
5282     %HTMLAttrStatus,
5283 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5284     abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5285 wakaba 1.49 align => FEATURE_M12N10_REC,
5286 wakaba 1.82 axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5287 wakaba 1.49 bgcolor => FEATURE_M12N10_REC_DEPRECATED,
5288     char => FEATURE_M12N10_REC,
5289     charoff => FEATURE_M12N10_REC,
5290 wakaba 1.82 colspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5291     headers => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5292 wakaba 1.49 height => FEATURE_M12N10_REC_DEPRECATED,
5293 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5294 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
5295 wakaba 1.82 rowspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5296     scope => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5297 wakaba 1.49 valign => FEATURE_M12N10_REC,
5298     width => FEATURE_M12N10_REC_DEPRECATED,
5299 wakaba 1.1 }),
5300     };
5301    
5302 wakaba 1.52 $Element->{$HTML_NS}->{form} = {
5303 wakaba 1.121 %HTMLFlowContentChecker,
5304 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5305 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5306 wakaba 1.56 accept => $AttrCheckerNotImplemented, ## TODO: ContentTypes [WF2]
5307 wakaba 1.129 'accept-charset' => $HTMLCharsetsAttrChecker,
5308 wakaba 1.52 action => $HTMLURIAttrChecker, ## TODO: "User agent behavior for a value other than HTTP URI is undefined" [HTML4]
5309 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
5310     enctype => $HTMLIMTAttrChecker, ## TODO: "multipart/form-data" should be used when type=file is used [HTML4] ## TODO: MUST NOT parameter [WF2]
5311     method => $GetHTMLEnumeratedAttrChecker->({
5312     get => 1, post => 1, put => 1, delete => 1,
5313     }),
5314 wakaba 1.52 ## NOTE: "get" SHOULD be used for idempotent submittion,
5315     ## "post" SHOULD be used otherwise [HTML4]. This cannot be tested.
5316 wakaba 1.133 name => sub {
5317     my ($self, $attr) = @_;
5318    
5319     my $value = $attr->value;
5320     if ($value eq '') {
5321     $self->{onerror}->(type => 'empty form name',
5322     node => $attr,
5323     level => $self->{level}->{must});
5324     } else {
5325     if ($self->{form}->{$value}) {
5326     $self->{onerror}->(type => 'duplicate form name',
5327     node => $attr,
5328     value => $value,
5329     level => $self->{level}->{must});
5330     } else {
5331     $self->{form}->{$value} = 1;
5332     }
5333     }
5334     },
5335 wakaba 1.126 onformchange => $HTMLEventHandlerAttrChecker,
5336     onforminput => $HTMLEventHandlerAttrChecker,
5337 wakaba 1.56 onreceived => $HTMLEventHandlerAttrChecker,
5338     replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
5339 wakaba 1.52 target => $HTMLTargetAttrChecker,
5340 wakaba 1.121 ## TODO: Warn for combination whose behavior is not defined in HTML4/WF2.
5341 wakaba 1.52 }, {
5342     %HTMLAttrStatus,
5343     %HTMLM12NCommonAttrStatus,
5344 wakaba 1.119 accept => FEATURE_WF2X | FEATURE_M12N10_REC,
5345     'accept-charset' => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5346     action => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5347 wakaba 1.56 data => FEATURE_WF2,
5348 wakaba 1.119 enctype => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5349 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5350 wakaba 1.119 method => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5351     #name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
5352     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5353 wakaba 1.126 onformchange => FEATURE_WF2_INFORMATIVE,
5354     onforminput => FEATURE_WF2_INFORMATIVE,
5355 wakaba 1.56 onreceived => FEATURE_WF2,
5356 wakaba 1.52 onreset => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5357     onsubmit => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5358 wakaba 1.56 replace => FEATURE_WF2,
5359 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
5360     sdasuff => FEATURE_HTML20_RFC,
5361 wakaba 1.119 target => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5362 wakaba 1.52 }),
5363 wakaba 1.66 check_start => sub {
5364     my ($self, $item, $element_state) = @_;
5365 wakaba 1.121 $self->_add_minus_elements ($element_state, {$HTML_NS => {form => 1}});
5366 wakaba 1.66
5367     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5368     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5369 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5370     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5371 wakaba 1.136 $element_state->{id_type} = 'form';
5372 wakaba 1.66 },
5373 wakaba 1.121 check_end => sub {
5374     my ($self, $item, $element_state) = @_;
5375     $self->_remove_minus_elements ($element_state);
5376    
5377     $HTMLFlowContentChecker{check_end}->(@_);
5378     },
5379 wakaba 1.52 };
5380    
5381     $Element->{$HTML_NS}->{fieldset} = {
5382 wakaba 1.134 %HTMLFlowContentChecker,
5383 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5384 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
5385     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5386 wakaba 1.136 form => $HTMLFormAttrChecker,
5387 wakaba 1.125 ## TODO: name [HTML5]
5388 wakaba 1.56 }, {
5389 wakaba 1.52 %HTMLAttrStatus,
5390     %HTMLM12NCommonAttrStatus,
5391 wakaba 1.119 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5392     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5393 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5394 wakaba 1.125 name => FEATURE_HTML5_DEFAULT,
5395 wakaba 1.52 }),
5396 wakaba 1.134 ## NOTE: legend, Flow
5397     check_child_element => sub {
5398     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5399     $child_is_transparent, $element_state) = @_;
5400     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
5401     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
5402     $self->{onerror}->(node => $child_el,
5403     type => 'element not allowed:minus',
5404     level => $self->{level}->{must});
5405     $element_state->{has_non_legend} = 1;
5406     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5407     #
5408     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') {
5409     if ($element_state->{has_non_legend}) {
5410     $self->{onerror}->(node => $child_el,
5411     type => 'element not allowed:details legend',
5412     level => $self->{level}->{must});
5413     }
5414     $element_state->{has_legend} = 1;
5415     $element_state->{has_non_legend} = 1;
5416     } else {
5417     $HTMLFlowContentChecker{check_child_element}->(@_);
5418     $element_state->{has_non_legend} = 1 unless $child_is_transparent;
5419     ## TODO:
5420     ## |<details><object><legend>xx</legend></object>..</details>|
5421     ## should be an error, since |object| is allowed as flow,
5422     ## therefore |details| part of the content model does not match.
5423     }
5424     },
5425     check_child_text => sub {
5426     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5427     if ($has_significant) {
5428     $element_state->{has_non_legend} = 1;
5429     }
5430     },
5431     check_end => sub {
5432     my ($self, $item, $element_state) = @_;
5433    
5434     unless ($element_state->{has_legend}) {
5435     $self->{onerror}->(node => $item->{node},
5436     type => 'child element missing',
5437     text => 'legend',
5438     level => $self->{level}->{must});
5439     }
5440    
5441     $HTMLFlowContentChecker{check_end}->(@_);
5442     ## ISSUE: |<details><legend>aa</legend></details>| error?
5443     },
5444     ## NOTE: This definition is partially reused by |details| element's
5445     ## checker.
5446 wakaba 1.52 ## TODO: Tests for <nest/> in <fieldset>
5447     };
5448    
5449     $Element->{$HTML_NS}->{input} = {
5450 wakaba 1.119 %HTMLEmptyChecker,
5451     status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5452 wakaba 1.140 check_attrs => sub {
5453     my ($self, $item, $element_state) = @_;
5454 wakaba 1.142
5455 wakaba 1.145 my $state = $item->{node}->get_attribute_ns (undef, 'type');
5456 wakaba 1.142 $state = 'text' unless defined $state;
5457     $state =~ tr/A-Z/a-z/; ## ASCII case-insensitive
5458    
5459 wakaba 1.140 for my $attr (@{$item->{node}->attributes}) {
5460     my $attr_ns = $attr->namespace_uri;
5461     $attr_ns = '' unless defined $attr_ns;
5462     my $attr_ln = $attr->manakai_local_name;
5463     my $checker;
5464     my $status;
5465     if ($attr_ns eq '') {
5466     $status =
5467     {
5468     %HTMLAttrStatus,
5469     %HTMLM12NCommonAttrStatus,
5470     accept => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5471     'accept-charset' => FEATURE_HTML2X_RFC,
5472     accesskey => FEATURE_M12N10_REC,
5473     action => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5474     align => FEATURE_M12N10_REC_DEPRECATED,
5475     alt => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5476     autocomplete => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5477     autofocus => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5478     checked => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5479     datafld => FEATURE_HTML4_REC_RESERVED,
5480     dataformatas => FEATURE_HTML4_REC_RESERVED,
5481     datasrc => FEATURE_HTML4_REC_RESERVED,
5482     disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5483     enctype => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5484     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5485 wakaba 1.150 inputmode => FEATURE_HTML5_DROPPED | FEATURE_WF2X |
5486     FEATURE_XHTMLBASIC11_CR,
5487 wakaba 1.140 ismap => FEATURE_M12N10_REC,
5488     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5489     list => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5490     max => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5491 wakaba 1.150 maxlength => FEATURE_HTML5_DEFAULT | FEATURE_WF2X |
5492     FEATURE_M12N10_REC,
5493 wakaba 1.140 method => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5494     min => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5495     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5496     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5497     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5498     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5499     onformchange => FEATURE_WF2_INFORMATIVE,
5500     onforminput => FEATURE_WF2_INFORMATIVE,
5501     oninput => FEATURE_WF2,
5502     oninvalid => FEATURE_WF2,
5503     onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5504     pattern => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5505     readonly => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5506     replace => FEATURE_WF2,
5507     required => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5508     sdapref => FEATURE_HTML20_RFC,
5509 wakaba 1.150 size => FEATURE_HTML5_DEFAULT | FEATURE_WF2X_DEPRECATED |
5510     FEATURE_M12N10_REC,
5511 wakaba 1.140 src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5512     step => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5513     tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5514     target => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5515 wakaba 1.141 template => FEATURE_HTML5_AT_RISK | FEATURE_WF2,
5516 wakaba 1.140 type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5517     usemap => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC,
5518     value => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5519     }->{$attr_ln};
5520    
5521     $checker =
5522     {
5523 wakaba 1.141 ## NOTE: Value of an empty string means that the attribute is only
5524     ## applicable for a specific set of states.
5525 wakaba 1.142 accept => '',
5526 wakaba 1.149 'accept-charset' => $HTMLCharsetsAttrChecker,
5527     ## NOTE: To which states it applies is not defined in RFC 2070.
5528 wakaba 1.150 accesskey => '', ## NOTE: Not applied to |hidden| [WF2].
5529 wakaba 1.142 action => '',
5530 wakaba 1.150 align => '',
5531 wakaba 1.141 alt => '',
5532 wakaba 1.142 autocomplete => '',
5533     autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
5534     ## ISSUE: No HTML5 definition yet. ## TODO: Tests
5535     checked => '',
5536     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5537     ## ISSUE: No HTML5 definition yet. ## TODO: Tests
5538     enctype => '',
5539     form => $HTMLFormAttrChecker,
5540 wakaba 1.150 inputmode => '',
5541     ismap => '', ## NOTE: "MUST" be type=image [HTML4]
5542 wakaba 1.142 list => '',
5543     max => '',
5544     maxlength => '',
5545     method => '',
5546     min => '',
5547     name => sub {
5548     ## ISSUE: No HTML5 definition yet.
5549     ## TODO: tests
5550     },
5551 wakaba 1.149 onformchange => $HTMLEventHandlerAttrChecker, # [WF2]
5552     onforminput => $HTMLEventHandlerAttrChecker, # [WF2]
5553     oninput => $HTMLEventHandlerAttrChecker, # [WF2]
5554     oninvalid => $HTMLEventHandlerAttrChecker, # [WF2]
5555     ## TODO: tests for four attributes above
5556 wakaba 1.142 pattern => '',
5557     readonly => '',
5558 wakaba 1.150 replace => '',
5559 wakaba 1.142 required => '',
5560     size => '',
5561     src => '',
5562     step => '',
5563     target => '',
5564 wakaba 1.140 type => $GetHTMLEnumeratedAttrChecker->({
5565 wakaba 1.141 hidden => 1, text => 1, email => 1, url => 1, password => 1,
5566     datetime => 1, date => 1, month => 1, week => 1, time => 1,
5567     'datetime-local' => 1, number => 1, range => 1, checkbox => 1,
5568     radio => 1, file => 1, submit => 1, image => 1, reset => 1,
5569     button => 1,
5570 wakaba 1.140 }),
5571 wakaba 1.151 usemap => '',
5572 wakaba 1.142 value => '',
5573 wakaba 1.140 }->{$attr_ln};
5574 wakaba 1.141
5575     ## State-dependent checkers
5576     unless ($checker) {
5577     if ($state eq 'hidden') {
5578     $checker =
5579     {
5580 wakaba 1.142 value => sub {
5581     my ($self, $attr, $item, $element_state) = @_;
5582 wakaba 1.145 my $name = $item->{node}->get_attribute_ns (undef, 'name');
5583 wakaba 1.142 if (defined $name and $name eq '_charset_') { ## case-sensitive
5584     $self->{onerror}->(node => $attr,
5585     type => '_charset_ value',
5586     level => $self->{level}->{must});
5587     }
5588     },
5589 wakaba 1.141 }->{$attr_ln} || $checker;
5590 wakaba 1.142 ## TODO: Warn if no name attribute?
5591     ## TODO: Warn if name!=_charset_ and no value attribute?
5592 wakaba 1.143 } elsif ($state eq 'datetime') {
5593     $checker =
5594     {
5595 wakaba 1.150 accesskey => $HTMLAccesskeyAttrChecker,
5596 wakaba 1.145 autocomplete => $GetHTMLEnumeratedAttrChecker->({
5597     on => 1, off => 1,
5598     }),
5599 wakaba 1.143 ## TODO: list
5600     ## TODO: max
5601     ## TODO: min
5602 wakaba 1.150 ## TODO: min <= max
5603 wakaba 1.145 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5604 wakaba 1.146 required => $GetHTMLBooleanAttrChecker->('required'),
5605 wakaba 1.148 step => $StepAttrChecker,
5606 wakaba 1.143 value => sub {
5607     ## NOTE: No restriction.
5608     ## TODO: Warn if not a valid UTC date and time string.
5609 wakaba 1.150 ## TODO: Warn unless min <= value <= max
5610 wakaba 1.143 },
5611     }->{$attr_ln} || $checker;
5612 wakaba 1.144 } elsif ($state eq 'date') {
5613     $checker =
5614     {
5615 wakaba 1.150 accesskey => $HTMLAccesskeyAttrChecker,
5616 wakaba 1.145 autocomplete => $GetHTMLEnumeratedAttrChecker->({
5617     on => 1, off => 1,
5618     }),
5619 wakaba 1.144 ## TODO: list
5620     ## TODO: max
5621     ## TODO: min
5622 wakaba 1.150 ## TODO: min <= max
5623 wakaba 1.145 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5624 wakaba 1.146 required => $GetHTMLBooleanAttrChecker->('required'),
5625 wakaba 1.148 step => $StepAttrChecker,
5626 wakaba 1.144 value => sub {
5627     ## NOTE: No restriction.
5628     ## TODO: Warn if not a valid date string.
5629 wakaba 1.150 ## TODO: Warn unless min <= value <= max
5630 wakaba 1.144 },
5631     }->{$attr_ln} || $checker;
5632     } elsif ($state eq 'month') {
5633     $checker =
5634     {
5635 wakaba 1.150 accesskey => $HTMLAccesskeyAttrChecker,
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.150 ## TODO: min <= max
5643 wakaba 1.145 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5644 wakaba 1.146 required => $GetHTMLBooleanAttrChecker->('required'),
5645 wakaba 1.148 step => $StepAttrChecker,
5646 wakaba 1.144 value => sub {
5647     ## NOTE: No restriction.
5648     ## TODO: Warn if not a valid month string.
5649 wakaba 1.150 ## TODO: Warn unless min <= value <= max
5650 wakaba 1.144 },
5651     }->{$attr_ln} || $checker;
5652     } elsif ($state eq 'week') {
5653     $checker =
5654     {
5655 wakaba 1.150 accesskey => $HTMLAccesskeyAttrChecker,
5656 wakaba 1.145 autocomplete => $GetHTMLEnumeratedAttrChecker->({
5657     on => 1, off => 1,
5658     }),
5659 wakaba 1.144 ## TODO: list
5660     ## TODO: max
5661     ## TODO: min
5662 wakaba 1.150 ## TODO: min <= max
5663 wakaba 1.145 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5664 wakaba 1.146 required => $GetHTMLBooleanAttrChecker->('required'),
5665 wakaba 1.148 step => $StepAttrChecker,
5666 wakaba 1.144 value => sub {
5667     ## NOTE: No restriction.
5668     ## TODO: Warn if not a valid week string.
5669 wakaba 1.150 ## TODO: Warn unless min <= value <= max
5670 wakaba 1.144 },
5671     }->{$attr_ln} || $checker;
5672     } elsif ($state eq 'time') {
5673     $checker =
5674     {
5675 wakaba 1.150 accesskey => $HTMLAccesskeyAttrChecker,
5676 wakaba 1.145 autocomplete => $GetHTMLEnumeratedAttrChecker->({
5677     on => 1, off => 1,
5678     }),
5679 wakaba 1.144 ## TODO: list
5680     ## TODO: max
5681     ## TODO: min
5682 wakaba 1.150 ## TODO: min <= max
5683 wakaba 1.145 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5684 wakaba 1.146 required => $GetHTMLBooleanAttrChecker->('required'),
5685 wakaba 1.148 step => $StepAttrChecker,
5686 wakaba 1.144 value => sub {
5687     ## NOTE: No restriction.
5688     ## TODO: Warn if not a valid time string.
5689 wakaba 1.150 ## TODO: Warn unless min <= value <= max
5690 wakaba 1.144 },
5691     }->{$attr_ln} || $checker;
5692     } elsif ($state eq 'datetime-local') {
5693     $checker =
5694     {
5695 wakaba 1.150 accesskey => $HTMLAccesskeyAttrChecker,
5696 wakaba 1.145 autocomplete => $GetHTMLEnumeratedAttrChecker->({
5697     on => 1, off => 1,
5698     }),
5699 wakaba 1.144 ## TODO: list
5700     ## TODO: max
5701     ## TODO: min
5702 wakaba 1.150 ## TODO: min <= max
5703 wakaba 1.145 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5704 wakaba 1.146 required => $GetHTMLBooleanAttrChecker->('required'),
5705 wakaba 1.148 step => $StepAttrChecker,
5706 wakaba 1.144 value => sub {
5707     ## NOTE: No restriction.
5708     ## TODO: Warn if not a valid local date and time string.
5709 wakaba 1.150 ## TODO: Warn unless min <= value <= max
5710 wakaba 1.144 },
5711     }->{$attr_ln} || $checker;
5712     } elsif ($state eq 'number') {
5713     $checker =
5714     {
5715 wakaba 1.150 accesskey => $HTMLAccesskeyAttrChecker,
5716 wakaba 1.145 autocomplete => $GetHTMLEnumeratedAttrChecker->({
5717     on => 1, off => 1,
5718     }),
5719 wakaba 1.144 ## TODO: list
5720     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
5721     min => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
5722     ## TODO: min & max tests
5723 wakaba 1.150 ## TODO: min <= max
5724 wakaba 1.145 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5725 wakaba 1.146 required => $GetHTMLBooleanAttrChecker->('required'),
5726 wakaba 1.148 step => $StepAttrChecker,
5727 wakaba 1.144 value => sub {
5728     ## NOTE: No restriction.
5729     ## TODO: Warn if not a valid floating point number.
5730 wakaba 1.150 ## TODO: Warn unless min <= value <= max
5731 wakaba 1.144 },
5732     }->{$attr_ln} || $checker;
5733     } elsif ($state eq 'range') {
5734     $checker =
5735     {
5736 wakaba 1.150 accesskey => $HTMLAccesskeyAttrChecker,
5737 wakaba 1.145 autocomplete => $GetHTMLEnumeratedAttrChecker->({
5738     on => 1, off => 1,
5739     }),
5740 wakaba 1.144 ## TODO: list
5741     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
5742     min => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
5743     ## TODO: min & max tests
5744 wakaba 1.150 ## TODO: min <= max
5745 wakaba 1.148 step => $StepAttrChecker,
5746 wakaba 1.144 value => sub {
5747     ## NOTE: No restriction.
5748     ## TODO: Warn if not a valid floating point number.
5749 wakaba 1.150 ## TODO: Warn unless min <= value <= max
5750 wakaba 1.144 },
5751     }->{$attr_ln} || $checker;
5752     } elsif ($state eq 'checkbox' or $state eq 'radio') {
5753     $checker =
5754     {
5755 wakaba 1.150 accesskey => $HTMLAccesskeyAttrChecker,
5756 wakaba 1.149 checked => $GetHTMLBooleanAttrChecker->('checked'),
5757     ## ISSUE: checked value not (yet?) defined.
5758     ## TODO: tests
5759 wakaba 1.146 required => $GetHTMLBooleanAttrChecker->('required'),
5760 wakaba 1.144 value => sub { }, ## NOTE: No restriction.
5761     }->{$attr_ln} || $checker;
5762     ## TODO: There MUST be another input type=radio with same
5763     ## name (Radio state).
5764     ## ISSUE: There should be exactly one type=radio with checked?
5765     } elsif ($state eq 'file') {
5766     $checker =
5767     {
5768     ## TODO: accept
5769 wakaba 1.150 accesskey => $HTMLAccesskeyAttrChecker,
5770 wakaba 1.146 required => $GetHTMLBooleanAttrChecker->('required'),
5771 wakaba 1.144 }->{$attr_ln} || $checker;
5772     } elsif ($state eq 'submit') {
5773     $checker =
5774     {
5775 wakaba 1.150 accesskey => $HTMLAccesskeyAttrChecker,
5776 wakaba 1.149 action => $HTMLURIAttrChecker,
5777     ## ISSUE: Not yet defined.
5778     ## TODO: tests
5779     enctype => $HTMLIMTAttrChecker,
5780     ## ISSUE: Not yet defined.
5781     ## TODO: tests
5782     method => $GetHTMLEnumeratedAttrChecker->({
5783     get => 1, post => 1, put => 1, delete => 1,
5784     }),
5785     ## ISSUE: Not yet defined.
5786     ## TODO: tests
5787     replace => $GetHTMLEnumeratedAttrChecker->({
5788     document => 1, values => 1,
5789     }),
5790     target => $HTMLTargetAttrChecker,
5791     ## ISSUE: Not yet defined.
5792     ## TODO: tests
5793 wakaba 1.144 value => sub { }, ## NOTE: No restriction.
5794     }->{$attr_ln} || $checker;
5795     } elsif ($state eq 'image') {
5796     $checker =
5797     {
5798 wakaba 1.150 accesskey => $HTMLAccesskeyAttrChecker,
5799 wakaba 1.149 action => $HTMLURIAttrChecker,
5800     ## ISSUE: Not yet defined.
5801     ## TODO: tests
5802     align => $GetHTMLEnumeratedAttrChecker->({
5803     top => 1, middle => 1, bottom => 1, left => 1, right => 1,
5804     }),
5805 wakaba 1.144 alt => sub {
5806     my ($self, $attr) = @_;
5807     my $value = $attr->value;
5808     unless (length $value) {
5809     $self->{onerror}->(node => $attr,
5810     type => 'empty anchor image alt',
5811     level => $self->{level}->{must});
5812     }
5813     },
5814 wakaba 1.149 enctype => $HTMLIMTAttrChecker,
5815     ## ISSUE: Not yet defined.
5816     ## TODO: tests
5817     ismap => $GetHTMLBooleanAttrChecker->('ismap'),
5818     method => $GetHTMLEnumeratedAttrChecker->({
5819     get => 1, post => 1, put => 1, delete => 1,
5820     }),
5821     ## ISSUE: Not yet defined.
5822     ## TODO: tests
5823     replace => $GetHTMLEnumeratedAttrChecker->({
5824     document => 1, values => 1,
5825     }),
5826 wakaba 1.144 src => $HTMLURIAttrChecker,
5827     ## TODO: There is requirements on the referenced resource.
5828 wakaba 1.149 target => $HTMLTargetAttrChecker,
5829     ## ISSUE: Not yet defined.
5830     ## TODO: tests
5831     usemap => $HTMLUsemapAttrChecker,
5832 wakaba 1.144 }->{$attr_ln} || $checker;
5833     ## TODO: alt & src are required.
5834     } elsif ({
5835     reset => 1, button => 1,
5836     ## NOTE: From Web Forms 2.0:
5837     remove => 1, 'move-up' => 1, 'move-down' => 1,
5838     add => 1,
5839     }->{$state}) {
5840     $checker =
5841     {
5842 wakaba 1.150 accesskey => $HTMLAccesskeyAttrChecker,
5843 wakaba 1.144 ## NOTE: According to Web Forms 2.0, |input| attribute
5844     ## has |template| attribute to support the |add| button
5845     ## type (as part of the repetition template feature). It
5846     ## conflicts with the |template| global attribute
5847     ## introduced as part of the data template feature.
5848     ## NOTE: |template| attribute as defined in Web Forms 2.0
5849     ## has no author requirement.
5850     value => sub { }, ## NOTE: No restriction.
5851     }->{$attr_ln} || $checker;
5852 wakaba 1.147 } else { # Text, E-mail, URL, Password
5853 wakaba 1.141 $checker =
5854     {
5855 wakaba 1.150 accesskey => $HTMLAccesskeyAttrChecker,
5856 wakaba 1.145 autocomplete => $GetHTMLEnumeratedAttrChecker->({
5857     on => 1, off => 1,
5858     }),
5859 wakaba 1.149 ## TODO: inputmode [WF2]
5860 wakaba 1.143 ## TODO: list
5861 wakaba 1.147 maxlength => sub {
5862     my ($self, $attr, $item, $element_state) = @_;
5863    
5864     $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 })->(@_);
5865    
5866     if ($attr->value =~ /^[\x09\x0A\x0D\x20]*([0-9]+)/) {
5867     ## NOTE: Applying the rules for parsing non-negative
5868     ## integers results in a number.
5869     my $max_allowed_value_length = 0+$1;
5870    
5871     my $value = $item->{node}->get_attribute_ns (undef, 'value');
5872     if (defined $value) {
5873     my $codepoint_length = length $value;
5874     $codepoint_length++
5875     while $value =~ /[\x{10000}-\x{10FFFF}]/g;
5876    
5877     if ($codepoint_length > $max_allowed_value_length) {
5878     $self->{onerror}
5879     ->(node => $item->{node}
5880     ->get_attribute_node_ns (undef, 'value'),
5881     type => 'value too long',
5882     level => $self->{level}->{must});
5883     }
5884     }
5885     }
5886     },
5887 wakaba 1.143 ## TODO: pattern
5888 wakaba 1.145 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5889 wakaba 1.146 required => $GetHTMLBooleanAttrChecker->('required'),
5890 wakaba 1.147 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub {shift > 0}),
5891 wakaba 1.143 value => sub {
5892     ## NOTE: No restriction.
5893     ## TODO: Warn if it contains CR or LF?
5894     ## TODO: Warn if not addr-spec (E-Mail state)?
5895     ## TODO: Warn if not a URL (URL state)?
5896     },
5897 wakaba 1.141 }->{$attr_ln} || $checker;
5898 wakaba 1.147 $checker = '' if $state eq 'password' and $attr_ln eq 'list';
5899 wakaba 1.141 }
5900     }
5901    
5902     if (defined $checker) {
5903     if ($checker eq '') {
5904     $checker = sub {
5905     my ($self, $attr) = @_;
5906     $self->{onerror}->(node => $attr,
5907     type => 'input attr not applicable',
5908     text => $state,
5909     level => $self->{level}->{must});
5910     };
5911     }
5912 wakaba 1.140 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and
5913     $attr_ln !~ /[A-Z]/) {
5914     $checker = $HTMLDatasetAttrChecker;
5915     $status = $HTMLDatasetAttrStatus;
5916     } else {
5917     $checker = $HTMLAttrChecker->{$attr_ln};
5918     }
5919     }
5920     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
5921     || $AttrChecker->{$attr_ns}->{''};
5922     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
5923     || $AttrStatus->{$attr_ns}->{''};
5924     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
5925    
5926     if ($checker) {
5927     $checker->($self, $attr, $item, $element_state) if ref $checker;
5928     } elsif ($attr_ns eq '' and not $status) {
5929     #
5930     } else {
5931     $self->{onerror}->(node => $attr,
5932     type => 'unknown attribute',
5933     level => $self->{level}->{uncertain});
5934     ## ISSUE: No comformance createria for unknown attributes in the spec
5935     }
5936    
5937     $self->_attr_status_info ($attr, $status);
5938     }
5939 wakaba 1.150
5940     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5941     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
5942     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
5943     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5944     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5945 wakaba 1.140 },
5946 wakaba 1.66 check_start => sub {
5947     my ($self, $item, $element_state) = @_;
5948 wakaba 1.139 if ($self->{flag}->{has_label} and $self->{flag}->{has_labelable}) {
5949     $self->{onerror}->(node => $item->{node},
5950     type => 'multiple labelable fae',
5951     level => $self->{level}->{must});
5952     } else {
5953     $self->{flag}->{has_labelable} = 2;
5954     }
5955 wakaba 1.138
5956     $element_state->{id_type} = 'labelable';
5957 wakaba 1.66 },
5958 wakaba 1.52 };
5959    
5960 wakaba 1.56 ## TODO: Form |name| attributes: MUST NOT conflict with RFC 3106 [WF2]
5961    
5962 wakaba 1.80 ## NOTE: "authors who are nesting repetition blocks should position such
5963     ## [repetition-block-related] buttons carefully to make clear which block a
5964 wakaba 1.150 ## button applies to." [WF2]: I have no idea how this can be tested.
5965 wakaba 1.80
5966 wakaba 1.52 $Element->{$HTML_NS}->{button} = {
5967 wakaba 1.119 %HTMLPhrasingContentChecker, ## ISSUE: -interactive?
5968     status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5969 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5970 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5971 wakaba 1.56 action => $HTMLURIAttrChecker,
5972     autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
5973 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5974 wakaba 1.136 form => $HTMLFormAttrChecker,
5975 wakaba 1.56 method => $GetHTMLEnumeratedAttrChecker->({
5976     get => 1, post => 1, put => 1, delete => 1,
5977     }),
5978 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
5979 wakaba 1.126 onformchange => $HTMLEventHandlerAttrChecker,
5980     onforminput => $HTMLEventHandlerAttrChecker,
5981 wakaba 1.56 replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
5982     target => $HTMLTargetAttrChecker,
5983 wakaba 1.80 ## NOTE: According to Web Forms 2.0, |button| attribute has |template|
5984     ## attribute to support the |add| button type (as part of repetition
5985     ## template feature). It conflicts with the |template| global attribute
5986     ## introduced as part of the data template feature.
5987     ## NOTE: |template| attribute as defined in Web Forms 2.0 has no
5988     ## author requirement.
5989 wakaba 1.52 type => $GetHTMLEnumeratedAttrChecker->({
5990     button => 1, submit => 1, reset => 1,
5991 wakaba 1.80 ## [WF2]
5992     add => 1, remove => 1, 'move-up' => 1, 'move-down' => 1,
5993 wakaba 1.52 }),
5994     value => sub {}, ## NOTE: CDATA [M12N]
5995     }, {
5996     %HTMLAttrStatus,
5997     %HTMLM12NCommonAttrStatus,
5998     accesskey => FEATURE_M12N10_REC,
5999 wakaba 1.119 action => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6000     autofocus => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6001 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
6002     dataformatas => FEATURE_HTML4_REC_RESERVED,
6003     datasrc => FEATURE_HTML4_REC_RESERVED,
6004 wakaba 1.119 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6005     enctype => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6006     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6007 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6008 wakaba 1.119 method => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6009     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6010 wakaba 1.52 onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6011     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6012 wakaba 1.126 onformchange => FEATURE_WF2_INFORMATIVE,
6013     onforminput => FEATURE_WF2_INFORMATIVE,
6014 wakaba 1.56 replace => FEATURE_WF2,
6015 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6016 wakaba 1.119 target => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6017 wakaba 1.150 template => FEATURE_HTML5_AT_RISK | FEATURE_WF2,
6018 wakaba 1.119 type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6019     value => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6020 wakaba 1.52 }),
6021 wakaba 1.66 check_start => sub {
6022     my ($self, $item, $element_state) = @_;
6023 wakaba 1.139 if ($self->{flag}->{has_label} and $self->{flag}->{has_labelable}) {
6024     $self->{onerror}->(node => $item->{node},
6025     type => 'multiple labelable fae',
6026     level => $self->{level}->{must});
6027     } else {
6028     $self->{flag}->{has_labelable} = 2;
6029     }
6030    
6031 wakaba 1.66 $element_state->{uri_info}->{action}->{type}->{action} = 1;
6032     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
6033 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6034     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6035 wakaba 1.138
6036     $element_state->{id_type} = 'labelable';
6037 wakaba 1.66 },
6038 wakaba 1.52 };
6039    
6040     $Element->{$HTML_NS}->{label} = {
6041 wakaba 1.139 %HTMLPhrasingContentChecker,
6042 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC
6043     | FEATURE_XHTML2_ED,
6044 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6045 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
6046 wakaba 1.138 for => sub {
6047     my ($self, $attr) = @_;
6048    
6049     ## NOTE: MUST be an ID of a labelable element.
6050    
6051     push @{$self->{idref}}, ['labelable', $attr->value, $attr];
6052     },
6053 wakaba 1.136 form => $HTMLFormAttrChecker,
6054 wakaba 1.52 }, {
6055     %HTMLAttrStatus,
6056 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
6057 wakaba 1.56 accesskey => FEATURE_WF2 | FEATURE_M12N10_REC,
6058 wakaba 1.119 for => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6059     form => FEATURE_HTML5_DEFAULT,
6060 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6061     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6062     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6063     }),
6064 wakaba 1.139 check_start => sub {
6065     my ($self, $item, $element_state) = @_;
6066     $self->_add_minus_elements ($element_state, {$HTML_NS => {label => 1}});
6067    
6068     $element_state->{has_label_original} = $self->{flag}->{has_label};
6069     $self->{flag}->{has_label} = 1;
6070     $element_state->{has_labelable_original} = $self->{flag}->{has_labelable};
6071     $self->{flag}->{has_labelable} = $item->{node}->has_attribute ('for')?1:0;
6072    
6073     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6074     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6075     },
6076     check_end => sub {
6077     my ($self, $item, $element_state) = @_;
6078     $self->_remove_minus_elements ($element_state);
6079    
6080     if ($self->{flag}->{has_labelable} == 1) { # has for="" but no labelable
6081     $self->{flag}->{has_labelable}
6082     = $element_state->{has_labelable_original};
6083     }
6084     delete $self->{flag}->{has_label}
6085     unless $element_state->{has_label_original};
6086     ## TODO: Warn if no labelable descendant? <input type=hidden>?
6087    
6088     ## NOTE: |<label for=a><input id=a></label>| is non-conforming.
6089    
6090     $HTMLPhrasingContentChecker{check_end}->(@_);
6091     },
6092 wakaba 1.52 ## TODO: Tests for <nest/> in <label>
6093     };
6094    
6095     $Element->{$HTML_NS}->{select} = {
6096 wakaba 1.121 %HTMLChecker,
6097 wakaba 1.52 ## TODO: author should SELECTED at least one OPTION in non-MULTIPLE case [HTML4].
6098     ## TODO: more than one OPTION with SELECTED in non-MULTIPLE case is "error" [HTML4]
6099 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6100 wakaba 1.56 is_root => 1, ## TODO: SHOULD NOT in application/xhtml+xml [WF2]
6101 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6102 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
6103 wakaba 1.56 autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
6104 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
6105 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
6106 wakaba 1.136 form => $HTMLFormAttrChecker,
6107 wakaba 1.52 multiple => $GetHTMLBooleanAttrChecker->('multiple'),
6108     name => sub {}, ## NOTE: CDATA [M12N]
6109 wakaba 1.126 onformchange => $HTMLEventHandlerAttrChecker,
6110     onforminput => $HTMLEventHandlerAttrChecker,
6111     oninput => $HTMLEventHandlerAttrChecker,
6112 wakaba 1.56 oninvalid => $HTMLEventHandlerAttrChecker,
6113 wakaba 1.52 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
6114     }, {
6115     %HTMLAttrStatus,
6116     %HTMLM12NCommonAttrStatus,
6117 wakaba 1.56 accesskey => FEATURE_WF2,
6118 wakaba 1.119 autofocus => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6119 wakaba 1.56 data => FEATURE_WF2,
6120 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
6121     dataformatas => FEATURE_HTML4_REC_RESERVED,
6122     datasrc => FEATURE_HTML4_REC_RESERVED,
6123 wakaba 1.119 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6124     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6125 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6126 wakaba 1.119 multiple => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6127     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6128 wakaba 1.52 onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6129     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6130 wakaba 1.126 onformchange => FEATURE_WF2_INFORMATIVE,
6131     onforminput => FEATURE_WF2_INFORMATIVE,
6132 wakaba 1.52 onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6133 wakaba 1.126 oninput => FEATURE_WF2,
6134 wakaba 1.56 oninvalid => FEATURE_WF2,
6135 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
6136     sdapref => FEATURE_HTML20_RFC,
6137 wakaba 1.119 size => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6138 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6139     }),
6140 wakaba 1.66 check_start => sub {
6141     my ($self, $item, $element_state) = @_;
6142 wakaba 1.139 if ($self->{flag}->{has_label} and $self->{flag}->{has_labelable}) {
6143     $self->{onerror}->(node => $item->{node},
6144     type => 'multiple labelable fae',
6145     level => $self->{level}->{must});
6146     } else {
6147     $self->{flag}->{has_labelable} = 2;
6148     }
6149 wakaba 1.66
6150     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
6151     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
6152 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6153     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6154 wakaba 1.138
6155     $element_state->{id_type} = 'labelable';
6156 wakaba 1.66 },
6157 wakaba 1.121 check_child_element => sub {
6158     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6159     $child_is_transparent, $element_state) = @_;
6160 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
6161     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
6162 wakaba 1.121 $self->{onerror}->(node => $child_el,
6163     type => 'element not allowed:minus',
6164     level => $self->{level}->{must});
6165     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6166     #
6167     } elsif ($child_nsuri eq $HTML_NS and
6168     {
6169     option => 1, optgroup => 1,
6170     }->{$child_ln}) {
6171     #
6172     } else {
6173     $self->{onerror}->(node => $child_el, type => 'element not allowed',
6174     level => $self->{level}->{must});
6175     }
6176     },
6177     check_child_text => sub {
6178     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6179     if ($has_significant) {
6180     $self->{onerror}->(node => $child_node, type => 'character not allowed',
6181     level => $self->{level}->{must});
6182     }
6183     },
6184 wakaba 1.52 };
6185 wakaba 1.1
6186 wakaba 1.52 $Element->{$HTML_NS}->{datalist} = {
6187 wakaba 1.121 %HTMLPhrasingContentChecker,
6188 wakaba 1.56 ## TODO: |option| child MUST be empty [WF2]
6189 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6190 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
6191     data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
6192     }, {
6193 wakaba 1.52 %HTMLAttrStatus,
6194 wakaba 1.56 data => FEATURE_WF2,
6195 wakaba 1.52 }),
6196 wakaba 1.66 check_start => sub {
6197     my ($self, $item, $element_state) = @_;
6198    
6199 wakaba 1.121 $element_state->{phase} = 'any'; # any | phrasing | option
6200    
6201 wakaba 1.66 $element_state->{uri_info}->{data}->{type}->{resource} = 1;
6202 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6203     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6204 wakaba 1.66 },
6205 wakaba 1.121 ## NOTE: phrasing | option*
6206     check_child_element => sub {
6207     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6208     $child_is_transparent, $element_state) = @_;
6209 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
6210     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
6211 wakaba 1.121 $self->{onerror}->(node => $child_el,
6212     type => 'element not allowed:minus',
6213     level => $self->{level}->{must});
6214     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6215     #
6216     } elsif ($element_state->{phase} eq 'phrasing') {
6217     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
6218     #
6219     } else {
6220     $self->{onerror}->(node => $child_el,
6221     type => 'element not allowed:phrasing',
6222     level => $self->{level}->{must});
6223     }
6224     } elsif ($element_state->{phase} eq 'option') {
6225     if ($child_nsuri eq $HTML_NS and $child_ln eq 'option') {
6226     #
6227     } else {
6228     $self->{onerror}->(node => $child_el,
6229     type => 'element not allowed',
6230     level => $self->{level}->{must});
6231     }
6232     } elsif ($element_state->{phase} eq 'any') {
6233     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
6234     $element_state->{phase} = 'phrasing';
6235     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'option') {
6236     $element_state->{phase} = 'option';
6237     } else {
6238     $self->{onerror}->(node => $child_el,
6239     type => 'element not allowed',
6240     level => $self->{level}->{must});
6241     }
6242     } else {
6243     die "check_child_element: Bad |datalist| phase: $element_state->{phase}";
6244     }
6245     },
6246     check_child_text => sub {
6247     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6248     if ($has_significant) {
6249     if ($element_state->{phase} eq 'phrasing') {
6250     #
6251     } elsif ($element_state->{phase} eq 'any') {
6252     $element_state->{phase} = 'phrasing';
6253     } else {
6254     $self->{onerror}->(node => $child_node,
6255     type => 'character not allowed',
6256     level => $self->{level}->{must});
6257     }
6258     }
6259     },
6260     check_end => sub {
6261     my ($self, $item, $element_state) = @_;
6262     if ($element_state->{phase} eq 'phrasing') {
6263     if ($element_state->{has_significant}) {
6264     $item->{real_parent_state}->{has_significant} = 1;
6265     } elsif ($item->{transparent}) {
6266     #
6267     } else {
6268     $self->{onerror}->(node => $item->{node},
6269     type => 'no significant content',
6270     level => $self->{level}->{should});
6271     }
6272     } else {
6273     ## NOTE: Since the content model explicitly allows a |datalist| element
6274     ## being empty, we don't raise "no significant content" error for this
6275     ## element when there is no element. (We should raise an error for
6276     ## |<datalist><br></datalist>|, however.)
6277     ## NOTE: As a side-effect, when the |datalist| element only contains
6278     ## non-conforming content, then the |phase| flag has not changed from
6279     ## |any|, no "no significant content" error is raised neither.
6280     $HTMLChecker{check_end}->(@_);
6281     }
6282     },
6283 wakaba 1.52 };
6284 wakaba 1.49
6285 wakaba 1.52 $Element->{$HTML_NS}->{optgroup} = {
6286 wakaba 1.121 %HTMLChecker,
6287 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6288 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6289     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
6290     label => sub {}, ## NOTE: Text [M12N] ## TODO: required
6291     }, {
6292     %HTMLAttrStatus,
6293     %HTMLM12NCommonAttrStatus,
6294 wakaba 1.119 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6295     label => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6296 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6297     }),
6298 wakaba 1.121 check_child_element => sub {
6299     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6300     $child_is_transparent, $element_state) = @_;
6301 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
6302     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
6303 wakaba 1.121 $self->{onerror}->(node => $child_el,
6304     type => 'element not allowed:minus',
6305     level => $self->{level}->{must});
6306     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6307     #
6308     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'option') {
6309     #
6310     } else {
6311     $self->{onerror}->(node => $child_el, type => 'element not allowed',
6312     level => $self->{level}->{must});
6313     }
6314     },
6315     check_child_text => sub {
6316     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6317     if ($has_significant) {
6318     $self->{onerror}->(node => $child_node, type => 'character not allowed',
6319     level => $self->{level}->{must});
6320     }
6321     },
6322 wakaba 1.52 };
6323    
6324     $Element->{$HTML_NS}->{option} = {
6325     %HTMLTextChecker,
6326 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6327 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6328     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
6329     label => sub {}, ## NOTE: Text [M12N]
6330     selected => $GetHTMLBooleanAttrChecker->('selected'),
6331     value => sub {}, ## NOTE: CDATA [M12N]
6332     }, {
6333     %HTMLAttrStatus,
6334     %HTMLM12NCommonAttrStatus,
6335 wakaba 1.119 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6336     label => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6337 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6338 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
6339     sdapref => FEATURE_HTML20_RFC,
6340 wakaba 1.119 selected => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6341     value => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6342 wakaba 1.52 }),
6343     };
6344 wakaba 1.49
6345 wakaba 1.52 $Element->{$HTML_NS}->{textarea} = {
6346     %HTMLTextChecker,
6347 wakaba 1.121 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6348 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6349 wakaba 1.56 accept => $HTMLIMTAttrChecker, ## TODO: MUST be a text-based type
6350 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
6351 wakaba 1.56 autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
6352     cols => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## TODO: SHOULD if wrap=hard [WF2]
6353 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
6354 wakaba 1.136 form => $HTMLFormAttrChecker,
6355 wakaba 1.56 ## TODO: inputmode [WF2]
6356     maxlength => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
6357 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
6358 wakaba 1.126 onformchange => $HTMLEventHandlerAttrChecker,
6359     onforminput => $HTMLEventHandlerAttrChecker,
6360 wakaba 1.127 oninput => $HTMLEventHandlerAttrChecker,
6361 wakaba 1.56 ## TODO: pattern [WF2] ## TODO: |title| special semantics
6362 wakaba 1.52 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
6363 wakaba 1.56 required => $GetHTMLBooleanAttrChecker->('required'),
6364     rows => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
6365 wakaba 1.126 oninput => $HTMLEventHandlerAttrChecker,
6366 wakaba 1.56 oninvalid => $HTMLEventHandlerAttrChecker,
6367     wrap => $GetHTMLEnumeratedAttrChecker->({soft => 1, hard => 1}),
6368 wakaba 1.52 }, {
6369     %HTMLAttrStatus,
6370     %HTMLM12NCommonAttrStatus,
6371 wakaba 1.121 accept => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6372 wakaba 1.61 'accept-charset' => FEATURE_HTML2X_RFC,
6373 wakaba 1.52 accesskey => FEATURE_M12N10_REC,
6374 wakaba 1.121 autofocus => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6375     cols => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6376 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
6377 wakaba 1.49 dataformatas => FEATURE_HTML4_REC_RESERVED,
6378     datasrc => FEATURE_HTML4_REC_RESERVED,
6379 wakaba 1.121 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6380     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6381     inputmode => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_XHTMLBASIC11_CR,
6382 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6383 wakaba 1.121 maxlength => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6384     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6385 wakaba 1.52 onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6386     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6387     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6388 wakaba 1.126 onformchange => FEATURE_WF2_INFORMATIVE,
6389     onforminput => FEATURE_WF2_INFORMATIVE,
6390     oninput => FEATURE_WF2,
6391 wakaba 1.56 oninvalid => FEATURE_WF2,
6392 wakaba 1.52 onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6393 wakaba 1.121 pattern => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6394     readonly => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6395     required => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6396     rows => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6397 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
6398     sdapref => FEATURE_HTML20_RFC,
6399 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6400 wakaba 1.121 wrap => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6401 wakaba 1.52 }),
6402 wakaba 1.66 check_start => sub {
6403     my ($self, $item, $element_state) = @_;
6404 wakaba 1.139 if ($self->{flag}->{has_label} and $self->{flag}->{has_labelable}) {
6405     $self->{onerror}->(node => $item->{node},
6406     type => 'multiple labelable fae',
6407     level => $self->{level}->{must});
6408     } else {
6409     $self->{flag}->{has_labelable} = 2;
6410     }
6411 wakaba 1.66
6412     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
6413 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6414     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6415 wakaba 1.138
6416     $element_state->{id_type} = 'labelable';
6417 wakaba 1.66 },
6418 wakaba 1.52 };
6419 wakaba 1.49
6420 wakaba 1.52 $Element->{$HTML_NS}->{output} = {
6421 wakaba 1.121 %HTMLPhrasingContentChecker,
6422     status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6423 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
6424     ## TODO: for [WF2]
6425 wakaba 1.136 form => $HTMLFormAttrChecker,
6426 wakaba 1.56 ## TODO: name [WF2]
6427 wakaba 1.126 onformchange => $HTMLEventHandlerAttrChecker,
6428     onforminput => $HTMLEventHandlerAttrChecker,
6429 wakaba 1.56 }, {
6430 wakaba 1.52 %HTMLAttrStatus,
6431 wakaba 1.121 for => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6432     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6433     name => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6434 wakaba 1.56 onchange => FEATURE_HTML5_DEFAULT | FEATURE_WF2,
6435     onformchange => FEATURE_WF2,
6436     onforminput => FEATURE_WF2,
6437 wakaba 1.52 }),
6438 wakaba 1.56 ## NOTE: "The output element should be used when ..." [WF2]
6439 wakaba 1.52 };
6440    
6441     $Element->{$HTML_NS}->{isindex} = {
6442     %HTMLEmptyChecker,
6443 wakaba 1.54 status => FEATURE_M12N10_REC_DEPRECATED |
6444     Whatpm::ContentChecker::FEATURE_DEPRECATED_SHOULD, ## [HTML4]
6445 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6446     prompt => sub {}, ## NOTE: Text [M12N]
6447     }, {
6448     %HTMLAttrStatus,
6449     class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6450     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6451     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6452     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6453     prompt => FEATURE_M12N10_REC_DEPRECATED,
6454 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
6455 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6456 wakaba 1.52 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6457     }),
6458     ## TODO: Tests
6459     ## TODO: Tests for <nest/> in <isindex>
6460 wakaba 1.66 check_start => sub {
6461     my ($self, $item, $element_state) = @_;
6462    
6463     $element_state->{uri_info}->{action}->{type}->{action} = 1;
6464 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6465     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6466 wakaba 1.66 },
6467 wakaba 1.52 };
6468 wakaba 1.49
6469 wakaba 1.1 $Element->{$HTML_NS}->{script} = {
6470 wakaba 1.40 %HTMLChecker,
6471 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6472 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6473 wakaba 1.91 charset => sub {
6474     my ($self, $attr) = @_;
6475    
6476     unless ($attr->owner_element->has_attribute_ns (undef, 'src')) {
6477     $self->{onerror}->(type => 'attribute not allowed',
6478     node => $attr,
6479 wakaba 1.104 level => $self->{level}->{must});
6480 wakaba 1.91 }
6481    
6482     $HTMLCharsetChecker->($attr->value, @_);
6483     },
6484 wakaba 1.86 language => sub {}, ## NOTE: No syntax constraint according to HTML4.
6485 wakaba 1.91 src => $HTMLURIAttrChecker, ## TODO: pointed resource MUST be in type of type="" (resource error)
6486 wakaba 1.1 defer => $GetHTMLBooleanAttrChecker->('defer'),
6487     async => $GetHTMLBooleanAttrChecker->('async'),
6488 wakaba 1.91 type => $HTMLIMTAttrChecker, ## TODO: MUST NOT: |charset=""| parameter
6489 wakaba 1.49 }, {
6490     %HTMLAttrStatus,
6491 wakaba 1.50 async => FEATURE_HTML5_DEFAULT,
6492 wakaba 1.91 charset => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6493 wakaba 1.50 defer => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6494 wakaba 1.49 event => FEATURE_HTML4_REC_RESERVED,
6495     for => FEATURE_HTML4_REC_RESERVED,
6496 wakaba 1.121 href => FEATURE_RDFA_PR,
6497 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6498 wakaba 1.49 language => FEATURE_M12N10_REC_DEPRECATED,
6499 wakaba 1.50 src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6500     type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6501 wakaba 1.9 }),
6502 wakaba 1.40 check_start => sub {
6503     my ($self, $item, $element_state) = @_;
6504 wakaba 1.1
6505 wakaba 1.40 if ($item->{node}->has_attribute_ns (undef, 'src')) {
6506     $element_state->{must_be_empty} = 1;
6507 wakaba 1.1 } else {
6508     ## NOTE: No content model conformance in HTML5 spec.
6509 wakaba 1.40 my $type = $item->{node}->get_attribute_ns (undef, 'type');
6510     my $language = $item->{node}->get_attribute_ns (undef, 'language');
6511 wakaba 1.1 if ((defined $type and $type eq '') or
6512     (defined $language and $language eq '')) {
6513     $type = 'text/javascript';
6514     } elsif (defined $type) {
6515     #
6516     } elsif (defined $language) {
6517     $type = 'text/' . $language;
6518     } else {
6519     $type = 'text/javascript';
6520     }
6521 wakaba 1.93
6522     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)]) {
6523     $type = "$1/$2";
6524     $type =~ tr/A-Z/a-z/; ## NOTE: ASCII case-insensitive
6525     ## TODO: Though we strip prameter here, it should not be ignored for the purpose of conformance checking...
6526     }
6527     $element_state->{script_type} = $type;
6528 wakaba 1.40 }
6529 wakaba 1.66
6530     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
6531 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6532     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6533 wakaba 1.107
6534     $element_state->{text} = '';
6535 wakaba 1.40 },
6536     check_child_element => sub {
6537     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6538     $child_is_transparent, $element_state) = @_;
6539 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
6540     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
6541 wakaba 1.40 $self->{onerror}->(node => $child_el,
6542     type => 'element not allowed:minus',
6543 wakaba 1.104 level => $self->{level}->{must});
6544 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6545     #
6546     } else {
6547     if ($element_state->{must_be_empty}) {
6548     $self->{onerror}->(node => $child_el,
6549 wakaba 1.104 type => 'element not allowed:empty',
6550     level => $self->{level}->{must});
6551 wakaba 1.40 }
6552     }
6553     },
6554     check_child_text => sub {
6555     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6556     if ($has_significant and
6557     $element_state->{must_be_empty}) {
6558     $self->{onerror}->(node => $child_node,
6559 wakaba 1.104 type => 'character not allowed:empty',
6560     level => $self->{level}->{must});
6561 wakaba 1.40 }
6562 wakaba 1.115 $element_state->{text} .= $child_node->data;
6563 wakaba 1.40 },
6564     check_end => sub {
6565     my ($self, $item, $element_state) = @_;
6566     unless ($element_state->{must_be_empty}) {
6567 wakaba 1.93 if ($element_state->{script_type} =~ m![+/][Xx][Mm][Ll]\z!) {
6568     ## NOTE: XML content should be checked by THIS instance of checker
6569     ## as part of normal tree validation.
6570 wakaba 1.104 $self->{onerror}->(node => $item->{node},
6571     type => 'XML script lang',
6572     text => $element_state->{script_type},
6573     level => $self->{level}->{uncertain});
6574     ## ISSUE: Should we raise some kind of error for
6575     ## <script type="text/xml">aaaaa</script>?
6576     ## NOTE: ^^^ This is why we throw an "uncertain" error.
6577 wakaba 1.93 } else {
6578     $self->{onsubdoc}->({s => $element_state->{text},
6579     container_node => $item->{node},
6580     media_type => $element_state->{script_type},
6581     is_char_string => 1});
6582     }
6583 wakaba 1.40
6584     $HTMLChecker{check_end}->(@_);
6585 wakaba 1.1 }
6586     },
6587 wakaba 1.91 ## TODO: There MUST be |type| unless the script type is JavaScript. (resource error)
6588     ## NOTE: "When used to include script data, the script data must be embedded
6589     ## inline, the format of the data must be given using the type attribute,
6590     ## and the src attribute must not be specified." - not testable.
6591     ## TODO: It would be possible to err <script type=text/plain src=...>
6592 wakaba 1.1 };
6593 wakaba 1.25 ## ISSUE: Significant check and text child node
6594 wakaba 1.1
6595     ## NOTE: When script is disabled.
6596     $Element->{$HTML_NS}->{noscript} = {
6597 wakaba 1.40 %HTMLTransparentChecker,
6598 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6599     check_attrs => $GetHTMLAttrsChecker->({}, {
6600     %HTMLAttrStatus,
6601     %HTMLM12NCommonAttrStatus,
6602 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6603 wakaba 1.49 }),
6604 wakaba 1.40 check_start => sub {
6605     my ($self, $item, $element_state) = @_;
6606 wakaba 1.3
6607 wakaba 1.40 unless ($item->{node}->owner_document->manakai_is_html) {
6608 wakaba 1.104 $self->{onerror}->(node => $item->{node}, type => 'in XML:noscript',
6609     level => $self->{level}->{must});
6610 wakaba 1.3 }
6611    
6612 wakaba 1.40 unless ($self->{flag}->{in_head}) {
6613     $self->_add_minus_elements ($element_state,
6614     {$HTML_NS => {noscript => 1}});
6615     }
6616 wakaba 1.79
6617     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6618     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6619 wakaba 1.3 },
6620 wakaba 1.40 check_child_element => sub {
6621     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6622     $child_is_transparent, $element_state) = @_;
6623     if ($self->{flag}->{in_head}) {
6624 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
6625     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
6626 wakaba 1.40 $self->{onerror}->(node => $child_el,
6627     type => 'element not allowed:minus',
6628 wakaba 1.104 level => $self->{level}->{must});
6629 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6630     #
6631     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'link') {
6632     #
6633     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') {
6634     if ($child_el->has_attribute_ns (undef, 'scoped')) {
6635     $self->{onerror}->(node => $child_el,
6636     type => 'element not allowed:head noscript',
6637 wakaba 1.104 level => $self->{level}->{must});
6638 wakaba 1.40 }
6639     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'meta') {
6640 wakaba 1.47 my $http_equiv_attr
6641     = $child_el->get_attribute_node_ns (undef, 'http-equiv');
6642     if ($http_equiv_attr) {
6643     ## TODO: case
6644     if (lc $http_equiv_attr->value eq 'content-type') {
6645 wakaba 1.40 $self->{onerror}->(node => $child_el,
6646 wakaba 1.34 type => 'element not allowed:head noscript',
6647 wakaba 1.104 level => $self->{level}->{must});
6648 wakaba 1.47 } else {
6649     #
6650 wakaba 1.3 }
6651 wakaba 1.47 } else {
6652     $self->{onerror}->(node => $child_el,
6653     type => 'element not allowed:head noscript',
6654 wakaba 1.104 level => $self->{level}->{must});
6655 wakaba 1.3 }
6656 wakaba 1.40 } else {
6657     $self->{onerror}->(node => $child_el,
6658     type => 'element not allowed:head noscript',
6659 wakaba 1.104 level => $self->{level}->{must});
6660 wakaba 1.40 }
6661     } else {
6662     $HTMLTransparentChecker{check_child_element}->(@_);
6663     }
6664     },
6665     check_child_text => sub {
6666     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6667     if ($self->{flag}->{in_head}) {
6668     if ($has_significant) {
6669     $self->{onerror}->(node => $child_node,
6670 wakaba 1.104 type => 'character not allowed',
6671     level => $self->{level}->{must});
6672 wakaba 1.3 }
6673     } else {
6674 wakaba 1.40 $HTMLTransparentChecker{check_child_text}->(@_);
6675     }
6676     },
6677     check_end => sub {
6678     my ($self, $item, $element_state) = @_;
6679     $self->_remove_minus_elements ($element_state);
6680     if ($self->{flag}->{in_head}) {
6681     $HTMLChecker{check_end}->(@_);
6682     } else {
6683     $HTMLPhrasingContentChecker{check_end}->(@_);
6684 wakaba 1.3 }
6685 wakaba 1.1 },
6686     };
6687 wakaba 1.3 ## ISSUE: Scripting is disabled: <head><noscript><html a></noscript></head>
6688 wakaba 1.1
6689     $Element->{$HTML_NS}->{'event-source'} = {
6690 wakaba 1.40 %HTMLEmptyChecker,
6691 wakaba 1.118 status => FEATURE_HTML5_LC_DROPPED,
6692     check_attrs => $GetHTMLAttrsChecker->({
6693     src => $HTMLURIAttrChecker,
6694     }, {
6695     %HTMLAttrStatus,
6696     src => FEATURE_HTML5_LC_DROPPED,
6697     }),
6698     check_start => sub {
6699     my ($self, $item, $element_state) = @_;
6700    
6701     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
6702     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6703     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6704     },
6705     };
6706    
6707     $Element->{$HTML_NS}->{eventsource} = {
6708     %HTMLEmptyChecker,
6709     status => FEATURE_HTML5_DEFAULT,
6710 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6711 wakaba 1.1 src => $HTMLURIAttrChecker,
6712 wakaba 1.50 }, {
6713     %HTMLAttrStatus,
6714 wakaba 1.118 src => FEATURE_HTML5_DEFAULT,
6715 wakaba 1.1 }),
6716 wakaba 1.66 check_start => sub {
6717     my ($self, $item, $element_state) = @_;
6718    
6719     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
6720 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6721     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6722 wakaba 1.66 },
6723 wakaba 1.1 };
6724    
6725     $Element->{$HTML_NS}->{details} = {
6726 wakaba 1.134 %{$Element->{$HTML_NS}->{fieldset}},
6727 wakaba 1.48 status => FEATURE_HTML5_WD,
6728 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6729 wakaba 1.1 open => $GetHTMLBooleanAttrChecker->('open'),
6730 wakaba 1.50 }, {
6731     %HTMLAttrStatus,
6732 wakaba 1.59 open => FEATURE_HTML5_WD,
6733 wakaba 1.1 }),
6734     };
6735    
6736     $Element->{$HTML_NS}->{datagrid} = {
6737 wakaba 1.72 %HTMLFlowContentChecker,
6738 wakaba 1.48 status => FEATURE_HTML5_WD,
6739 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6740 wakaba 1.1 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
6741     multiple => $GetHTMLBooleanAttrChecker->('multiple'),
6742 wakaba 1.50 }, {
6743     %HTMLAttrStatus,
6744     disabled => FEATURE_HTML5_WD,
6745     multiple => FEATURE_HTML5_WD,
6746 wakaba 1.1 }),
6747 wakaba 1.40 check_start => sub {
6748     my ($self, $item, $element_state) = @_;
6749 wakaba 1.1
6750 wakaba 1.40 $self->_add_minus_elements ($element_state,
6751     {$HTML_NS => {a => 1, datagrid => 1}});
6752     $element_state->{phase} = 'any';
6753 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6754     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6755 wakaba 1.40 },
6756 wakaba 1.95 ## NOTE: Flow -(text* (table|select|datalist) Flow*) | table | select |
6757     ## datalist | Empty
6758 wakaba 1.40 check_child_element => sub {
6759     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6760     $child_is_transparent, $element_state) = @_;
6761 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
6762     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
6763 wakaba 1.40 $self->{onerror}->(node => $child_el,
6764     type => 'element not allowed:minus',
6765 wakaba 1.104 level => $self->{level}->{must});
6766 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6767     #
6768 wakaba 1.72 } elsif ($element_state->{phase} eq 'flow') {
6769     if ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) {
6770 wakaba 1.44 if (not $element_state->{has_element} and
6771 wakaba 1.40 $child_nsuri eq $HTML_NS and
6772 wakaba 1.95 {
6773     table => 1, select => 1, datalist => 1,
6774     }->{$child_ln}) {
6775 wakaba 1.40 $self->{onerror}->(node => $child_el,
6776 wakaba 1.104 type => 'element not allowed',
6777     level => $self->{level}->{must});
6778 wakaba 1.40 } else {
6779 wakaba 1.8 #
6780 wakaba 1.1 }
6781 wakaba 1.40 } else {
6782     $self->{onerror}->(node => $child_el,
6783 wakaba 1.121 type => 'element not allowed', ## TODO: :flow
6784 wakaba 1.104 level => $self->{level}->{must});
6785 wakaba 1.40 }
6786 wakaba 1.43 $element_state->{has_element} = 1;
6787 wakaba 1.40 } elsif ($element_state->{phase} eq 'any') {
6788     if ($child_nsuri eq $HTML_NS and
6789     {table => 1, select => 1, datalist => 1}->{$child_ln}) {
6790     $element_state->{phase} = 'none';
6791 wakaba 1.72 } elsif ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) {
6792 wakaba 1.40 $element_state->{has_element} = 1;
6793 wakaba 1.72 $element_state->{phase} = 'flow';
6794 wakaba 1.40 } else {
6795     $self->{onerror}->(node => $child_el,
6796 wakaba 1.104 type => 'element not allowed',
6797     level => $self->{level}->{must});
6798 wakaba 1.40 }
6799     } elsif ($element_state->{phase} eq 'none') {
6800     $self->{onerror}->(node => $child_el,
6801 wakaba 1.104 type => 'element not allowed',
6802     level => $self->{level}->{must});
6803 wakaba 1.40 } else {
6804     die "check_child_element: Bad |datagrid| phase: $element_state->{phase}";
6805     }
6806     },
6807     check_child_text => sub {
6808     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6809     if ($has_significant) {
6810 wakaba 1.72 if ($element_state->{phase} eq 'flow') {
6811 wakaba 1.40 #
6812     } elsif ($element_state->{phase} eq 'any') {
6813 wakaba 1.72 $element_state->{phase} = 'flow';
6814 wakaba 1.40 } else {
6815     $self->{onerror}->(node => $child_node,
6816 wakaba 1.104 type => 'character not allowed',
6817     level => $self->{level}->{must});
6818 wakaba 1.1 }
6819     }
6820 wakaba 1.40 },
6821     check_end => sub {
6822     my ($self, $item, $element_state) = @_;
6823     $self->_remove_minus_elements ($element_state);
6824 wakaba 1.1
6825 wakaba 1.95 if ($element_state->{phase} eq 'flow') {
6826     if ($element_state->{has_significant}) {
6827     $item->{real_parent_state}->{has_significant} = 1;
6828     } elsif ($item->{transparent}) {
6829     #
6830     } else {
6831     $self->{onerror}->(node => $item->{node},
6832 wakaba 1.104 type => 'no significant content',
6833 wakaba 1.110 level => $self->{level}->{should});
6834 wakaba 1.95 }
6835     } else {
6836     ## NOTE: Since the content model explicitly allows a |datagird| element
6837     ## being empty, we don't raise "no significant content" error for this
6838     ## element when there is no element. (We should raise an error for
6839     ## |<datagrid><br></datagrid>|, however.)
6840     ## NOTE: As a side-effect, when the |datagrid| element only contains
6841     ## non-conforming content, then the |phase| flag has not changed from
6842     ## |any|, no "no significant content" error is raised neither.
6843     ## NOTE: Another side-effect of the current implementation:
6844     ## |<daragrid><datagrid/></datagrid>| has no "no significant content"
6845     ## error at all.
6846 wakaba 1.40 $HTMLChecker{check_end}->(@_);
6847     }
6848     },
6849 wakaba 1.1 };
6850    
6851     $Element->{$HTML_NS}->{command} = {
6852 wakaba 1.40 %HTMLEmptyChecker,
6853 wakaba 1.48 status => FEATURE_HTML5_WD,
6854 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6855 wakaba 1.1 checked => $GetHTMLBooleanAttrChecker->('checked'),
6856     default => $GetHTMLBooleanAttrChecker->('default'),
6857     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
6858     icon => $HTMLURIAttrChecker,
6859     label => sub { }, ## NOTE: No conformance creteria
6860     radiogroup => sub { }, ## NOTE: No conformance creteria
6861     type => sub {
6862     my ($self, $attr) = @_;
6863     my $value = $attr->value;
6864     unless ({command => 1, checkbox => 1, radio => 1}->{$value}) {
6865 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'invalid attribute value',
6866     level => $self->{level}->{must});
6867 wakaba 1.1 }
6868     },
6869 wakaba 1.50 }, {
6870     %HTMLAttrStatus,
6871     checked => FEATURE_HTML5_WD,
6872     default => FEATURE_HTML5_WD,
6873     disabled => FEATURE_HTML5_WD,
6874     icon => FEATURE_HTML5_WD,
6875     label => FEATURE_HTML5_WD,
6876     radiogroup => FEATURE_HTML5_WD,
6877     type => FEATURE_HTML5_WD,
6878 wakaba 1.1 }),
6879 wakaba 1.66 check_start => sub {
6880     my ($self, $item, $element_state) = @_;
6881    
6882     $element_state->{uri_info}->{icon}->{type}->{embedded} = 1;
6883 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6884     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6885 wakaba 1.66 },
6886 wakaba 1.115 };
6887    
6888     $Element->{$HTML_NS}->{bb} = {
6889     %HTMLPhrasingContentChecker,
6890     status => FEATURE_HTML5_DEFAULT,
6891     check_attrs => $GetHTMLAttrsChecker->({
6892     type => $GetHTMLEnumeratedAttrChecker->({makeapp => 1}),
6893     }, {
6894     %HTMLAttrStatus,
6895     type => FEATURE_HTML5_DEFAULT,
6896     }),
6897 wakaba 1.130 check_start => sub {
6898     my ($self, $item, $element_state) = @_;
6899     $self->_add_minus_elements ($element_state, $HTMLInteractiveContent);
6900    
6901     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6902     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6903     },
6904     check_end => sub {
6905     my ($self, $item, $element_state) = @_;
6906     $self->_remove_minus_elements ($element_state);
6907    
6908     $HTMLTransparentChecker{check_end}->(@_);
6909     },
6910 wakaba 1.1 };
6911    
6912     $Element->{$HTML_NS}->{menu} = {
6913 wakaba 1.40 %HTMLPhrasingContentChecker,
6914 wakaba 1.54 #status => FEATURE_M12N10_REC_DEPRECATED | FEATURE_HTML5_WD,
6915     status => FEATURE_M12N10_REC | FEATURE_HTML5_WD,
6916     ## NOTE: We don't want any |menu| element warned as deprecated.
6917 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6918 wakaba 1.1 autosubmit => $GetHTMLBooleanAttrChecker->('autosubmit'),
6919 wakaba 1.68 compact => $GetHTMLBooleanAttrChecker->('compact'),
6920 wakaba 1.135 ## ISSUE: <menu id=""><p contextmenu=""> match? (In the current
6921     ## implementation, it does not match.)
6922 wakaba 1.1 label => sub { }, ## NOTE: No conformance creteria
6923     type => $GetHTMLEnumeratedAttrChecker->({context => 1, toolbar => 1}),
6924 wakaba 1.49 }, {
6925     %HTMLAttrStatus,
6926     %HTMLM12NCommonAttrStatus,
6927 wakaba 1.61 align => FEATURE_HTML2X_RFC,
6928 wakaba 1.113 autosubmit => FEATURE_HTML5_DROPPED,
6929 wakaba 1.49 compat => FEATURE_M12N10_REC_DEPRECATED,
6930 wakaba 1.50 label => FEATURE_HTML5_WD,
6931     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6932 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
6933     sdapref => FEATURE_HTML20_RFC,
6934 wakaba 1.50 type => FEATURE_HTML5_WD,
6935 wakaba 1.1 }),
6936 wakaba 1.40 check_start => sub {
6937     my ($self, $item, $element_state) = @_;
6938     $element_state->{phase} = 'li or phrasing';
6939     $element_state->{in_menu_original} = $self->{flag}->{in_menu};
6940     $self->{flag}->{in_menu} = 1;
6941 wakaba 1.79
6942     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6943     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6944 wakaba 1.135 $element_state->{id_type} = 'menu';
6945 wakaba 1.40 },
6946     check_child_element => sub {
6947     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6948     $child_is_transparent, $element_state) = @_;
6949 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
6950     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
6951 wakaba 1.40 $self->{onerror}->(node => $child_el,
6952     type => 'element not allowed:minus',
6953 wakaba 1.104 level => $self->{level}->{must});
6954 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6955     #
6956     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'li') {
6957     if ($element_state->{phase} eq 'li') {
6958     #
6959     } elsif ($element_state->{phase} eq 'li or phrasing') {
6960     $element_state->{phase} = 'li';
6961     } else {
6962 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
6963     level => $self->{level}->{must});
6964 wakaba 1.40 }
6965     } elsif ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
6966     if ($element_state->{phase} eq 'phrasing') {
6967     #
6968     } elsif ($element_state->{phase} eq 'li or phrasing') {
6969     $element_state->{phase} = 'phrasing';
6970     } else {
6971 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
6972     level => $self->{level}->{must});
6973 wakaba 1.40 }
6974     } else {
6975 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
6976     level => $self->{level}->{must});
6977 wakaba 1.40 }
6978     },
6979     check_child_text => sub {
6980     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6981     if ($has_significant) {
6982     if ($element_state->{phase} eq 'phrasing') {
6983     #
6984     } elsif ($element_state->{phase} eq 'li or phrasing') {
6985     $element_state->{phase} = 'phrasing';
6986     } else {
6987     $self->{onerror}->(node => $child_node,
6988 wakaba 1.104 type => 'character not allowed',
6989     level => $self->{level}->{must});
6990 wakaba 1.1 }
6991     }
6992 wakaba 1.40 },
6993     check_end => sub {
6994     my ($self, $item, $element_state) = @_;
6995     delete $self->{flag}->{in_menu} unless $element_state->{in_menu_original};
6996    
6997     if ($element_state->{phase} eq 'li') {
6998     $HTMLChecker{check_end}->(@_);
6999     } else { # 'phrasing' or 'li or phrasing'
7000     $HTMLPhrasingContentChecker{check_end}->(@_);
7001 wakaba 1.1 }
7002     },
7003 wakaba 1.8 };
7004    
7005     $Element->{$HTML_NS}->{datatemplate} = {
7006 wakaba 1.40 %HTMLChecker,
7007 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
7008 wakaba 1.40 check_child_element => sub {
7009     my ($self, $item, $child_el, $child_nsuri, $child_ln,
7010     $child_is_transparent, $element_state) = @_;
7011 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
7012     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
7013 wakaba 1.40 $self->{onerror}->(node => $child_el,
7014     type => 'element not allowed:minus',
7015 wakaba 1.104 level => $self->{level}->{must});
7016 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
7017     #
7018     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'rule') {
7019     #
7020     } else {
7021     $self->{onerror}->(node => $child_el,
7022 wakaba 1.104 type => 'element not allowed:datatemplate',
7023     level => $self->{level}->{must});
7024 wakaba 1.40 }
7025     },
7026     check_child_text => sub {
7027     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
7028     if ($has_significant) {
7029 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
7030     level => $self->{level}->{must});
7031 wakaba 1.8 }
7032     },
7033     is_xml_root => 1,
7034     };
7035    
7036     $Element->{$HTML_NS}->{rule} = {
7037 wakaba 1.40 %HTMLChecker,
7038 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
7039 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
7040 wakaba 1.23 condition => $HTMLSelectorsAttrChecker,
7041 wakaba 1.92 mode => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(),
7042 wakaba 1.50 }, {
7043     %HTMLAttrStatus,
7044     condition => FEATURE_HTML5_AT_RISK,
7045     mode => FEATURE_HTML5_AT_RISK,
7046 wakaba 1.8 }),
7047 wakaba 1.40 check_start => sub {
7048     my ($self, $item, $element_state) = @_;
7049 wakaba 1.79
7050 wakaba 1.40 $self->_add_plus_elements ($element_state, {$HTML_NS => {nest => 1}});
7051 wakaba 1.79 $element_state->{in_rule_original} = $self->{flag}->{in_rule};
7052     $self->{flag}->{in_rule} = 1;
7053    
7054     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
7055     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
7056 wakaba 1.40 },
7057     check_child_element => sub { },
7058     check_child_text => sub { },
7059     check_end => sub {
7060     my ($self, $item, $element_state) = @_;
7061 wakaba 1.79
7062 wakaba 1.40 $self->_remove_plus_elements ($element_state);
7063 wakaba 1.79 delete $self->{flag}->{in_rule} unless $element_state->{in_rule_original};
7064    
7065 wakaba 1.40 $HTMLChecker{check_end}->(@_);
7066 wakaba 1.8 },
7067     ## NOTE: "MAY be anything that, when the parent |datatemplate|
7068     ## is applied to some conforming data, results in a conforming DOM tree.":
7069     ## We don't check against this.
7070     };
7071    
7072     $Element->{$HTML_NS}->{nest} = {
7073 wakaba 1.40 %HTMLEmptyChecker,
7074 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
7075 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
7076 wakaba 1.23 filter => $HTMLSelectorsAttrChecker,
7077     mode => sub {
7078     my ($self, $attr) = @_;
7079     my $value = $attr->value;
7080 wakaba 1.132 if ($value !~ /\A[^\x09\x0A\x0C\x0D\x20]+\z/) {
7081 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'mode:syntax error',
7082     level => $self->{level}->{must});
7083 wakaba 1.23 }
7084     },
7085 wakaba 1.50 }, {
7086     %HTMLAttrStatus,
7087     filter => FEATURE_HTML5_AT_RISK,
7088     mode => FEATURE_HTML5_AT_RISK,
7089 wakaba 1.8 }),
7090 wakaba 1.1 };
7091    
7092     $Element->{$HTML_NS}->{legend} = {
7093 wakaba 1.40 %HTMLPhrasingContentChecker,
7094 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
7095 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
7096 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
7097 wakaba 1.52 # align => $GetHTMLEnumeratedAttrChecker->({
7098     # top => 1, bottom => 1, left => 1, right => 1,
7099     # }),
7100     }, {
7101 wakaba 1.49 %HTMLAttrStatus,
7102     %HTMLM12NCommonAttrStatus,
7103     accesskey => FEATURE_M12N10_REC,
7104     align => FEATURE_M12N10_REC_DEPRECATED,
7105 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
7106 wakaba 1.49 }),
7107 wakaba 1.1 };
7108    
7109     $Element->{$HTML_NS}->{div} = {
7110 wakaba 1.72 %HTMLFlowContentChecker,
7111 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
7112 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
7113     align => $GetHTMLEnumeratedAttrChecker->({
7114     left => 1, center => 1, right => 1, justify => 1,
7115     }),
7116     }, {
7117 wakaba 1.49 %HTMLAttrStatus,
7118 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
7119 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
7120     datafld => FEATURE_HTML4_REC_RESERVED,
7121     dataformatas => FEATURE_HTML4_REC_RESERVED,
7122     datasrc => FEATURE_HTML4_REC_RESERVED,
7123 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
7124 wakaba 1.49 }),
7125 wakaba 1.66 check_start => sub {
7126     my ($self, $item, $element_state) = @_;
7127    
7128     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
7129 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
7130     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
7131 wakaba 1.66 },
7132 wakaba 1.1 };
7133    
7134 wakaba 1.64 $Element->{$HTML_NS}->{center} = {
7135 wakaba 1.72 %HTMLFlowContentChecker,
7136 wakaba 1.64 status => FEATURE_M12N10_REC_DEPRECATED,
7137     check_attrs => $GetHTMLAttrsChecker->({}, {
7138     %HTMLAttrStatus,
7139     %HTMLM12NCommonAttrStatus,
7140     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
7141     }),
7142     };
7143    
7144 wakaba 1.1 $Element->{$HTML_NS}->{font} = {
7145 wakaba 1.40 %HTMLTransparentChecker,
7146 wakaba 1.78 status => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC_DEPRECATED,
7147 wakaba 1.70 check_attrs => $GetHTMLAttrsChecker->({
7148     ## TODO: HTML4 |size|, |color|, |face|
7149 wakaba 1.49 }, {
7150     %HTMLAttrStatus,
7151 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
7152 wakaba 1.49 color => FEATURE_M12N10_REC_DEPRECATED,
7153 wakaba 1.50 dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
7154 wakaba 1.49 face => FEATURE_M12N10_REC_DEPRECATED,
7155 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
7156     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
7157 wakaba 1.49 size => FEATURE_M12N10_REC_DEPRECATED,
7158 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
7159 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
7160 wakaba 1.49 }),
7161 wakaba 1.78 ## NOTE: When the |font| element was defined in the HTML5 specification,
7162     ## it is allowed only in a document with the WYSIWYG signature. The
7163     ## checker does not check whether there is the signature, since the
7164     ## signature is dropped, too, and has never been implemented. (In addition,
7165     ## for any |font| element an "element not defined" error is raised anyway,
7166     ## such that we don't have to raise an additional error.)
7167 wakaba 1.1 };
7168 wakaba 1.49
7169 wakaba 1.64 $Element->{$HTML_NS}->{basefont} = {
7170     %HTMLEmptyChecker,
7171     status => FEATURE_M12N10_REC_DEPRECATED,
7172     check_attrs => $GetHTMLAttrsChecker->({
7173     ## TODO: color, face, size
7174     }, {
7175     %HTMLAttrStatus,
7176     color => FEATURE_M12N10_REC_DEPRECATED,
7177     face => FEATURE_M12N10_REC_DEPRECATED,
7178     #id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
7179     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
7180     size => FEATURE_M12N10_REC_DEPRECATED,
7181     }),
7182     };
7183    
7184 wakaba 1.49 ## TODO: frameset FEATURE_M12N10_REC
7185     ## class title id cols rows onload onunload style(x10)
7186     ## frame frameborder longdesc marginheight marginwidth noresize scrolling src name(deprecated) class,id,title,style(x10)
7187     ## noframes Common, lang(xhtml10)
7188    
7189 wakaba 1.100 ## TODO: CR: rbc rtc @rbspan (M12NXHTML2Common)
7190 wakaba 1.56
7191 wakaba 1.61 ## TODO: xmp, listing, plaintext FEATURE_HTML32_REC_OBSOLETE
7192     ## TODO: ^^^ lang, dir, id, class [HTML 2.x] sdaform [HTML 2.0]
7193     ## xmp, listing sdapref[HTML2,0]
7194    
7195 wakaba 1.56 =pod
7196    
7197     WF2: Documents MUST comply to [CHARMOD].
7198     WF2: Vencor extensions MUST NOT be used.
7199    
7200 wakaba 1.61 HTML 2.0 nextid @n
7201    
7202     RFC 2659: CERTS CRYPTOPTS
7203    
7204     ISO-HTML: pre-html, divN
7205 wakaba 1.82
7206     XHTML2: blockcode (Common), h (Common), separator (Common), l (Common),
7207     di (Common), nl (Common), handler (Common, type), standby (Common),
7208     summary (Common)
7209    
7210 wakaba 1.97 Access & XHTML2: access (LC)
7211 wakaba 1.82
7212     XML Events & XForms (for XHTML2 support; very, very low priority)
7213 wakaba 1.61
7214 wakaba 1.56 =cut
7215 wakaba 1.61
7216     ## NOTE: Where RFC 2659 allows additional attributes is unclear.
7217     ## We added them only to |a|. |link| and |form| might also allow them
7218     ## in theory.
7219 wakaba 1.1
7220     $Whatpm::ContentChecker::Namespace->{$HTML_NS}->{loaded} = 1;
7221    
7222     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24