/[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.141 - (hide annotations) (download)
Mon Oct 6 05:22:56 2008 UTC (16 years, 9 months ago) by wakaba
Branch: MAIN
Changes since 1.140: +92 -60 lines
++ whatpm/t/dom-conformance/ChangeLog	6 Oct 2008 05:18:43 -0000
2008-10-06  Wakaba  <wakaba@suika.fam.cx>

	* html-form-input-1.dat: Tests on <input type> added.

	* html-repetitions.dat: Redundant tests removed.

++ whatpm/Whatpm/ContentChecker/ChangeLog	6 Oct 2008 05:19:26 -0000
	* HTML.pm: <input type> definition updated according to HTML5
	definition.  Prepares for tests against <input type> states.

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

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24