/[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.90 - (hide annotations) (download)
Fri May 16 11:03:58 2008 UTC (17 years, 2 months ago) by wakaba
Branch: MAIN
Changes since 1.89: +6 -3 lines
++ whatpm/t/ChangeLog	16 May 2008 10:45:12 -0000
2008-05-16  Wakaba  <wakaba@suika.fam.cx>

	* content-model-2.dat: Test data for pixeldata="" attribute
	are added (HTML5 revision 1629).

++ whatpm/Whatpm/ContentChecker/ChangeLog	16 May 2008 10:50:31 -0000
2008-05-16  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm ($GetHTMLFloatingPointNumberAttrChecker): More than
	one "." characters were allowed in a floating point number
	incorrectly.
	(source): Support for the pixelratio="" attribute (HTML5 revision
	1629).

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24