/[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.89 - (hide annotations) (download)
Sat May 10 10:06:49 2008 UTC (17 years, 2 months ago) by wakaba
Branch: MAIN
Changes since 1.88: +8 -3 lines
++ whatpm/Whatpm/ContentChecker/ChangeLog	10 May 2008 10:06:23 -0000
	* HTML.pm: Yay, |canvas| got the final status!

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

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24