/[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.85 - (hide annotations) (download)
Mon May 5 09:52:18 2008 UTC (16 years, 6 months ago) by wakaba
Branch: MAIN
Changes since 1.84: +38 -7 lines
++ whatpm/t/ChangeLog	5 May 2008 09:51:44 -0000
	* content-model-1.dat, content-model-2.dat: New test data
	for meta refresh; some test results updated.  Duplicate
	meta http-equiv="" are now non-conforming.

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	5 May 2008 09:52:11 -0000
	* HTML.pm: Make duplicate http-equiv="" non-conforming.
	Check meta refresh content="".

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

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24