/[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.121 - (hide annotations) (download)
Fri Sep 5 16:15:28 2008 UTC (16 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.120: +175 -46 lines
++ whatpm/t/ChangeLog	5 Sep 2008 16:07:29 -0000
2008-09-06  Wakaba  <wakaba@suika.fam.cx>

	* content-model-1.dat: Form test data are added (cf. HTML5
	revisions 2142-2155).

++ whatpm/Whatpm/ContentChecker/ChangeLog	5 Sep 2008 16:08:26 -0000
2008-09-06  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: Support for form elements (HTML5 revisions
	2142, 2148, 2150-2154).

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

	* HTML.pm: RDFa spec status updated again.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24