/[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.84 - (hide annotations) (download)
Mon May 5 08:28:19 2008 UTC (18 years, 5 months ago) by wakaba
Branch: MAIN
Changes since 1.83: +4 -21 lines
++ whatpm/t/ChangeLog	5 May 2008 08:12:12 -0000
	* table-1.dat: Test data updated and some new test data
	added for new requirements (HTML5 revisions 1387 and 1388).

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	5 May 2008 08:14:26 -0000
	* HTML.pm: Empty <tbod>, <thead>, <tfoot>, and <tr> are
	no longer content model error (they might be raise
	table model error, however).  All table model error
	parameters should propagate.

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     if ({
1845     'refresh' => 1,
1846     'default-style' => 1,
1847     }->{$keyword}) {
1848     #
1849 wakaba 1.33
1850     ## TODO: More than one occurence is a MUST-level error (revision 1180).
1851 wakaba 1.32 } elsif ($keyword eq 'content-type') {
1852 wakaba 1.58 ## TODO: refs in "text/html; charset=" are not disallowed since rev.1275.
1853 wakaba 1.33
1854 wakaba 1.32 $check_charset_decl->();
1855     if ($content_attr) {
1856     my $content = $content_attr->value;
1857 wakaba 1.58 if ($content =~ m!^[Tt][Ee][Xx][Tt]/[Hh][Tt][Mm][Ll];
1858     [\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
1859     =(.+)\z!sx) {
1860 wakaba 1.32 $check_charset->($content_attr, $1);
1861     } else {
1862     $self->{onerror}->(node => $content_attr,
1863     type => 'meta content-type syntax error',
1864     level => $self->{must_level});
1865     }
1866     }
1867     } else {
1868     $self->{onerror}->(node => $http_equiv_attr,
1869     type => 'enumerated:invalid');
1870     }
1871     }
1872    
1873     if (defined $charset_attr) {
1874     $check_charset_decl->();
1875     $check_charset->($charset_attr, $charset_attr->value);
1876 wakaba 1.1 }
1877     },
1878     };
1879    
1880     $Element->{$HTML_NS}->{style} = {
1881 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1882 wakaba 1.40 %HTMLChecker,
1883     check_attrs => $GetHTMLAttrsChecker->({
1884 wakaba 1.1 type => $HTMLIMTAttrChecker, ## TODO: MUST be a styling language
1885     media => $HTMLMQAttrChecker,
1886     scoped => $GetHTMLBooleanAttrChecker->('scoped'),
1887     ## NOTE: |title| has special semantics for |style|s, but is syntactically
1888     ## not different
1889 wakaba 1.49 }, {
1890     %HTMLAttrStatus,
1891 wakaba 1.82 %XHTML2CommonAttrStatus,
1892     dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1893     disabled => FEATURE_XHTML2_ED,
1894     id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
1895 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1896 wakaba 1.82 media => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1897 wakaba 1.50 scoped => FEATURE_HTML5_DEFAULT,
1898 wakaba 1.82 title => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1899     type => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1900 wakaba 1.1 }),
1901 wakaba 1.40 check_start => sub {
1902     my ($self, $item, $element_state) = @_;
1903    
1904 wakaba 1.27 ## NOTE: |html:style| itself has no conformance creteria on content model.
1905 wakaba 1.40 my $type = $item->{node}->get_attribute_ns (undef, 'type');
1906 wakaba 1.27 if (not defined $type or
1907     $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]) {
1908 wakaba 1.40 $element_state->{allow_element} = 0;
1909     $element_state->{style_type} = 'text/css';
1910     } else {
1911     $element_state->{allow_element} = 1; # unknown
1912     $element_state->{style_type} = $type; ## TODO: $type normalization
1913     }
1914 wakaba 1.79
1915     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
1916     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
1917 wakaba 1.40 },
1918     check_child_element => sub {
1919     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1920     $child_is_transparent, $element_state) = @_;
1921     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
1922     $self->{onerror}->(node => $child_el,
1923     type => 'element not allowed:minus',
1924     level => $self->{must_level});
1925     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1926     #
1927     } elsif ($element_state->{allow_element}) {
1928     #
1929     } else {
1930     $self->{onerror}->(node => $child_el, type => 'element not allowed');
1931     }
1932     },
1933     check_child_text => sub {
1934     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1935     $element_state->{text} .= $child_node->text_content;
1936     },
1937     check_end => sub {
1938     my ($self, $item, $element_state) = @_;
1939     if ($element_state->{style_type} eq 'text/css') {
1940     $self->{onsubdoc}->({s => $element_state->{text},
1941     container_node => $item->{node},
1942 wakaba 1.28 media_type => 'text/css', is_char_string => 1});
1943 wakaba 1.27 } else {
1944 wakaba 1.40 $self->{onerror}->(node => $item->{node}, level => 'unsupported',
1945     type => 'style:'.$element_state->{style_type});
1946 wakaba 1.27 }
1947 wakaba 1.40
1948     $HTMLChecker{check_end}->(@_);
1949 wakaba 1.1 },
1950     };
1951 wakaba 1.25 ## ISSUE: Relationship to significant content check?
1952 wakaba 1.1
1953     $Element->{$HTML_NS}->{body} = {
1954 wakaba 1.72 %HTMLFlowContentChecker,
1955 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1956 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
1957     alink => $HTMLColorAttrChecker,
1958     background => $HTMLURIAttrChecker,
1959     bgcolor => $HTMLColorAttrChecker,
1960     link => $HTMLColorAttrChecker,
1961     text => $HTMLColorAttrChecker,
1962     vlink => $HTMLColorAttrChecker,
1963     }, {
1964 wakaba 1.49 %HTMLAttrStatus,
1965 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
1966 wakaba 1.49 alink => FEATURE_M12N10_REC_DEPRECATED,
1967     background => FEATURE_M12N10_REC_DEPRECATED,
1968     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
1969 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1970 wakaba 1.49 link => FEATURE_M12N10_REC_DEPRECATED,
1971 wakaba 1.50 onload => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1972     onunload => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1973 wakaba 1.49 text => FEATURE_M12N10_REC_DEPRECATED,
1974     vlink => FEATURE_M12N10_REC_DEPRECATED,
1975     }),
1976 wakaba 1.68 check_start => sub {
1977     my ($self, $item, $element_state) = @_;
1978    
1979     $element_state->{uri_info}->{background}->{type}->{embedded} = 1;
1980 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
1981     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
1982 wakaba 1.68 },
1983 wakaba 1.1 };
1984    
1985     $Element->{$HTML_NS}->{section} = {
1986 wakaba 1.72 %HTMLFlowContentChecker,
1987 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED,
1988     check_attrs => $GetHTMLAttrsChecker->({
1989     }, {
1990     %HTMLAttrStatus,
1991     %XHTML2CommonAttrStatus,
1992     }),
1993 wakaba 1.1 };
1994    
1995     $Element->{$HTML_NS}->{nav} = {
1996 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
1997 wakaba 1.72 %HTMLFlowContentChecker,
1998 wakaba 1.1 };
1999    
2000     $Element->{$HTML_NS}->{article} = {
2001 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2002 wakaba 1.72 %HTMLFlowContentChecker,
2003 wakaba 1.1 };
2004    
2005     $Element->{$HTML_NS}->{blockquote} = {
2006 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2007 wakaba 1.72 %HTMLFlowContentChecker,
2008 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2009 wakaba 1.1 cite => $HTMLURIAttrChecker,
2010 wakaba 1.49 }, {
2011     %HTMLAttrStatus,
2012 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2013 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2014 wakaba 1.82 cite => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2015 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2016 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2017 wakaba 1.1 }),
2018 wakaba 1.66 check_start => sub {
2019     my ($self, $item, $element_state) = @_;
2020    
2021     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
2022 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2023     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2024 wakaba 1.66 },
2025 wakaba 1.1 };
2026    
2027     $Element->{$HTML_NS}->{aside} = {
2028 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2029 wakaba 1.72 %HTMLFlowContentChecker,
2030 wakaba 1.1 };
2031    
2032     $Element->{$HTML_NS}->{h1} = {
2033 wakaba 1.40 %HTMLPhrasingContentChecker,
2034 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2035 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2036     align => $GetHTMLEnumeratedAttrChecker->({
2037     left => 1, center => 1, right => 1, justify => 1,
2038     }),
2039     }, {
2040 wakaba 1.49 %HTMLAttrStatus,
2041 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2042 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
2043 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2044 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2045 wakaba 1.49 }),
2046 wakaba 1.40 check_start => sub {
2047     my ($self, $item, $element_state) = @_;
2048     $self->{flag}->{has_hn} = 1;
2049 wakaba 1.79
2050     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2051     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2052 wakaba 1.1 },
2053     };
2054    
2055 wakaba 1.40 $Element->{$HTML_NS}->{h2} = {%{$Element->{$HTML_NS}->{h1}}};
2056 wakaba 1.1
2057 wakaba 1.40 $Element->{$HTML_NS}->{h3} = {%{$Element->{$HTML_NS}->{h1}}};
2058 wakaba 1.1
2059 wakaba 1.40 $Element->{$HTML_NS}->{h4} = {%{$Element->{$HTML_NS}->{h1}}};
2060 wakaba 1.1
2061 wakaba 1.40 $Element->{$HTML_NS}->{h5} = {%{$Element->{$HTML_NS}->{h1}}};
2062 wakaba 1.1
2063 wakaba 1.40 $Element->{$HTML_NS}->{h6} = {%{$Element->{$HTML_NS}->{h1}}};
2064 wakaba 1.1
2065 wakaba 1.29 ## TODO: Explicit sectioning is "encouraged".
2066    
2067 wakaba 1.1 $Element->{$HTML_NS}->{header} = {
2068 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2069 wakaba 1.72 %HTMLFlowContentChecker,
2070 wakaba 1.40 check_start => sub {
2071     my ($self, $item, $element_state) = @_;
2072     $self->_add_minus_elements ($element_state,
2073     {$HTML_NS => {qw/header 1 footer 1/}},
2074 wakaba 1.58 $HTMLSectioningContent);
2075 wakaba 1.40 $element_state->{has_hn_original} = $self->{flag}->{has_hn};
2076     $self->{flag}->{has_hn} = 0;
2077 wakaba 1.79
2078     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2079     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2080 wakaba 1.40 },
2081     check_end => sub {
2082     my ($self, $item, $element_state) = @_;
2083     $self->_remove_minus_elements ($element_state);
2084     unless ($self->{flag}->{has_hn}) {
2085     $self->{onerror}->(node => $item->{node},
2086     type => 'element missing:hn');
2087     }
2088     $self->{flag}->{has_hn} ||= $element_state->{has_hn_original};
2089 wakaba 1.1
2090 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
2091 wakaba 1.1 },
2092 wakaba 1.40 ## ISSUE: <header><del><h1>...</h1></del></header> is conforming?
2093 wakaba 1.1 };
2094    
2095     $Element->{$HTML_NS}->{footer} = {
2096 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2097 wakaba 1.72 %HTMLFlowContentChecker,
2098 wakaba 1.40 check_start => sub {
2099     my ($self, $item, $element_state) = @_;
2100     $self->_add_minus_elements ($element_state,
2101     {$HTML_NS => {footer => 1}},
2102 wakaba 1.58 $HTMLSectioningContent,
2103 wakaba 1.57 $HTMLHeadingContent);
2104 wakaba 1.79
2105     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2106     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2107 wakaba 1.40 },
2108     check_end => sub {
2109     my ($self, $item, $element_state) = @_;
2110     $self->_remove_minus_elements ($element_state);
2111 wakaba 1.1
2112 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
2113 wakaba 1.1 },
2114     };
2115    
2116     $Element->{$HTML_NS}->{address} = {
2117 wakaba 1.72 %HTMLFlowContentChecker,
2118 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2119 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2120     %HTMLAttrStatus,
2121 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2122 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2123 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2124 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2125     sdapref => FEATURE_HTML20_RFC,
2126 wakaba 1.49 }),
2127 wakaba 1.40 check_start => sub {
2128     my ($self, $item, $element_state) = @_;
2129     $self->_add_minus_elements ($element_state,
2130     {$HTML_NS => {footer => 1, address => 1}},
2131     $HTMLSectioningContent, $HTMLHeadingContent);
2132 wakaba 1.79
2133     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2134     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2135 wakaba 1.40 },
2136     check_end => sub {
2137     my ($self, $item, $element_state) = @_;
2138     $self->_remove_minus_elements ($element_state);
2139 wakaba 1.29
2140 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
2141 wakaba 1.29 },
2142 wakaba 1.1 };
2143    
2144     $Element->{$HTML_NS}->{p} = {
2145 wakaba 1.40 %HTMLPhrasingContentChecker,
2146 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2147 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2148     align => $GetHTMLEnumeratedAttrChecker->({
2149     left => 1, center => 1, right => 1, justify => 1,
2150     }),
2151     }, {
2152 wakaba 1.49 %HTMLAttrStatus,
2153 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2154 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
2155 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2156 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2157 wakaba 1.49 }),
2158 wakaba 1.1 };
2159    
2160     $Element->{$HTML_NS}->{hr} = {
2161 wakaba 1.40 %HTMLEmptyChecker,
2162 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2163 wakaba 1.70 check_attrs => $GetHTMLAttrsChecker->({
2164     ## TODO: HTML4 |align|, |noshade|, |size|, |width|
2165     }, {
2166 wakaba 1.49 %HTMLAttrStatus,
2167     %HTMLM12NCommonAttrStatus,
2168     align => FEATURE_M12N10_REC_DEPRECATED,
2169 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2170 wakaba 1.49 noshade => FEATURE_M12N10_REC_DEPRECATED,
2171 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
2172 wakaba 1.49 size => FEATURE_M12N10_REC_DEPRECATED,
2173     width => FEATURE_M12N10_REC_DEPRECATED,
2174     }),
2175 wakaba 1.1 };
2176    
2177     $Element->{$HTML_NS}->{br} = {
2178 wakaba 1.40 %HTMLEmptyChecker,
2179 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2180 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2181     clear => $GetHTMLEnumeratedAttrChecker->({
2182     left => 1, all => 1, right => 1, none => 1,
2183     }),
2184     }, {
2185 wakaba 1.49 %HTMLAttrStatus,
2186 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2187 wakaba 1.49 clear => FEATURE_M12N10_REC_DEPRECATED,
2188 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2189 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
2190 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2191 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2192 wakaba 1.49 }),
2193 wakaba 1.29 ## NOTE: Blank line MUST NOT be used for presentation purpose.
2194     ## (This requirement is semantic so that we cannot check.)
2195 wakaba 1.1 };
2196    
2197     $Element->{$HTML_NS}->{dialog} = {
2198 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2199 wakaba 1.40 %HTMLChecker,
2200     check_start => sub {
2201     my ($self, $item, $element_state) = @_;
2202     $element_state->{phase} = 'before dt';
2203 wakaba 1.79
2204     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2205     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2206 wakaba 1.40 },
2207     check_child_element => sub {
2208     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2209     $child_is_transparent, $element_state) = @_;
2210     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
2211     $self->{onerror}->(node => $child_el,
2212     type => 'element not allowed:minus',
2213     level => $self->{must_level});
2214     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2215     #
2216     } elsif ($element_state->{phase} eq 'before dt') {
2217     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2218     $element_state->{phase} = 'before dd';
2219     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2220     $self->{onerror}
2221     ->(node => $child_el, type => 'ps element missing:dt');
2222     $element_state->{phase} = 'before dt';
2223     } else {
2224     $self->{onerror}->(node => $child_el, type => 'element not allowed');
2225     }
2226     } elsif ($element_state->{phase} eq 'before dd') {
2227     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2228     $element_state->{phase} = 'before dt';
2229     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2230     $self->{onerror}
2231     ->(node => $child_el, type => 'ps element missing:dd');
2232     $element_state->{phase} = 'before dd';
2233     } else {
2234     $self->{onerror}->(node => $child_el, type => 'element not allowed');
2235 wakaba 1.1 }
2236 wakaba 1.40 } else {
2237     die "check_child_element: Bad |dialog| phase: $element_state->{phase}";
2238     }
2239     },
2240     check_child_text => sub {
2241     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2242     if ($has_significant) {
2243     $self->{onerror}->(node => $child_node, type => 'character not allowed');
2244 wakaba 1.1 }
2245 wakaba 1.40 },
2246     check_end => sub {
2247     my ($self, $item, $element_state) = @_;
2248     if ($element_state->{phase} eq 'before dd') {
2249     $self->{onerror}->(node => $item->{node},
2250     type => 'child element missing:dd');
2251 wakaba 1.1 }
2252 wakaba 1.40
2253     $HTMLChecker{check_end}->(@_);
2254 wakaba 1.1 },
2255     };
2256    
2257     $Element->{$HTML_NS}->{pre} = {
2258 wakaba 1.40 %HTMLPhrasingContentChecker,
2259 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2260 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2261     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
2262     }, {
2263 wakaba 1.49 %HTMLAttrStatus,
2264 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2265 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2266 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2267 wakaba 1.49 width => FEATURE_M12N10_REC_DEPRECATED,
2268     }),
2269 wakaba 1.1 };
2270    
2271     $Element->{$HTML_NS}->{ol} = {
2272 wakaba 1.40 %HTMLChecker,
2273 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2274 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2275 wakaba 1.68 compact => $GetHTMLBooleanAttrChecker->('compact'),
2276 wakaba 1.69 reversed => $GetHTMLBooleanAttrChecker->('reversed'),
2277 wakaba 1.1 start => $HTMLIntegerAttrChecker,
2278 wakaba 1.69 ## TODO: HTML4 |type|
2279 wakaba 1.49 }, {
2280     %HTMLAttrStatus,
2281 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2282 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2283 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2284 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2285 wakaba 1.53 reversed => FEATURE_HTML5_DEFAULT,
2286 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2287 wakaba 1.54 #start => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
2288     start => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2289 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2290 wakaba 1.1 }),
2291 wakaba 1.40 check_child_element => sub {
2292     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2293     $child_is_transparent, $element_state) = @_;
2294     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
2295     $self->{onerror}->(node => $child_el,
2296     type => 'element not allowed:minus',
2297     level => $self->{must_level});
2298     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2299     #
2300     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'li') {
2301     #
2302     } else {
2303     $self->{onerror}->(node => $child_el, type => 'element not allowed');
2304 wakaba 1.1 }
2305 wakaba 1.40 },
2306     check_child_text => sub {
2307     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2308     if ($has_significant) {
2309     $self->{onerror}->(node => $child_node, type => 'character not allowed');
2310 wakaba 1.1 }
2311     },
2312     };
2313    
2314     $Element->{$HTML_NS}->{ul} = {
2315 wakaba 1.40 %{$Element->{$HTML_NS}->{ol}},
2316 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2317 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2318     compact => $GetHTMLBooleanAttrChecker->('compact'),
2319 wakaba 1.69 ## TODO: HTML4 |type|
2320     ## TODO: sdaform, align
2321 wakaba 1.68 }, {
2322 wakaba 1.49 %HTMLAttrStatus,
2323 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2324 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2325 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2326 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2327 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2328 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2329     }),
2330 wakaba 1.1 };
2331    
2332 wakaba 1.64 $Element->{$HTML_NS}->{dir} = {
2333     ## TODO: %block; is not allowed [HTML4] ## TODO: Empty list allowed?
2334     %{$Element->{$HTML_NS}->{ul}},
2335     status => FEATURE_M12N10_REC_DEPRECATED,
2336 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2337     compact => $GetHTMLBooleanAttrChecker->('compact'),
2338     }, {
2339 wakaba 1.64 %HTMLAttrStatus,
2340     %HTMLM12NCommonAttrStatus,
2341     align => FEATURE_HTML2X_RFC,
2342     compact => FEATURE_M12N10_REC_DEPRECATED,
2343     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2344     sdaform => FEATURE_HTML20_RFC,
2345     sdapref => FEATURE_HTML20_RFC,
2346     }),
2347     };
2348    
2349 wakaba 1.1 $Element->{$HTML_NS}->{li} = {
2350 wakaba 1.72 %HTMLFlowContentChecker,
2351 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2352 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2353 wakaba 1.69 ## TODO: HTML4 |type|
2354 wakaba 1.49 value => sub {
2355 wakaba 1.1 my ($self, $attr) = @_;
2356     my $parent = $attr->owner_element->manakai_parent_element;
2357     if (defined $parent) {
2358     my $parent_ns = $parent->namespace_uri;
2359     $parent_ns = '' unless defined $parent_ns;
2360     my $parent_ln = $parent->manakai_local_name;
2361     unless ($parent_ns eq $HTML_NS and $parent_ln eq 'ol') {
2362     $self->{onerror}->(node => $attr, level => 'unsupported',
2363     type => 'attribute');
2364     }
2365     }
2366     $HTMLIntegerAttrChecker->($self, $attr);
2367 wakaba 1.49 }, ## TODO: test
2368     }, {
2369     %HTMLAttrStatus,
2370 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2371 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2372 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2373 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2374 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2375 wakaba 1.55 #value => FEATURE_HTML5_DEFAULT | FEATURE_XHTMLBASIC11_CR |
2376     # FEATURE_M12N10_REC_DEPRECATED,
2377 wakaba 1.82 value => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED |
2378     FEATURE_XHTMLBASIC11_CR | FEATURE_M12N10_REC,
2379 wakaba 1.1 }),
2380 wakaba 1.40 check_child_element => sub {
2381     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2382     $child_is_transparent, $element_state) = @_;
2383     if ($self->{flag}->{in_menu}) {
2384     $HTMLPhrasingContentChecker{check_child_element}->(@_);
2385     } else {
2386 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
2387 wakaba 1.40 }
2388     },
2389     check_child_text => sub {
2390     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2391     if ($self->{flag}->{in_menu}) {
2392     $HTMLPhrasingContentChecker{check_child_text}->(@_);
2393 wakaba 1.1 } else {
2394 wakaba 1.72 $HTMLFlowContentChecker{check_child_text}->(@_);
2395 wakaba 1.1 }
2396     },
2397     };
2398    
2399     $Element->{$HTML_NS}->{dl} = {
2400 wakaba 1.40 %HTMLChecker,
2401 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2402 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2403     compact => $GetHTMLBooleanAttrChecker->('compact'),
2404     }, {
2405 wakaba 1.49 %HTMLAttrStatus,
2406 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2407 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2408 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2409 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2410     sdapref => FEATURE_HTML20_RFC,
2411 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2412     }),
2413 wakaba 1.40 check_start => sub {
2414     my ($self, $item, $element_state) = @_;
2415     $element_state->{phase} = 'before dt';
2416 wakaba 1.79
2417     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2418     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2419 wakaba 1.40 },
2420     check_child_element => sub {
2421     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2422     $child_is_transparent, $element_state) = @_;
2423     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
2424     $self->{onerror}->(node => $child_el,
2425     type => 'element not allowed:minus',
2426     level => $self->{must_level});
2427     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2428     #
2429     } elsif ($element_state->{phase} eq 'in dds') {
2430     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2431     #$element_state->{phase} = 'in dds';
2432     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2433     $element_state->{phase} = 'in dts';
2434     } else {
2435     $self->{onerror}->(node => $child_el, type => 'element not allowed');
2436     }
2437     } elsif ($element_state->{phase} eq 'in dts') {
2438     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2439     #$element_state->{phase} = 'in dts';
2440     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2441     $element_state->{phase} = 'in dds';
2442     } else {
2443     $self->{onerror}->(node => $child_el, type => 'element not allowed');
2444     }
2445     } elsif ($element_state->{phase} eq 'before dt') {
2446     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2447     $element_state->{phase} = 'in dts';
2448     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2449     $self->{onerror}
2450     ->(node => $child_el, type => 'ps element missing:dt');
2451     $element_state->{phase} = 'in dds';
2452     } else {
2453     $self->{onerror}->(node => $child_el, type => 'element not allowed');
2454 wakaba 1.1 }
2455 wakaba 1.40 } else {
2456     die "check_child_element: Bad |dl| phase: $element_state->{phase}";
2457 wakaba 1.1 }
2458 wakaba 1.40 },
2459     check_child_text => sub {
2460     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2461     if ($has_significant) {
2462     $self->{onerror}->(node => $child_node, type => 'character not allowed');
2463     }
2464     },
2465     check_end => sub {
2466     my ($self, $item, $element_state) = @_;
2467     if ($element_state->{phase} eq 'in dts') {
2468     $self->{onerror}->(node => $item->{node},
2469     type => 'child element missing:dd');
2470 wakaba 1.1 }
2471    
2472 wakaba 1.40 $HTMLChecker{check_end}->(@_);
2473 wakaba 1.1 },
2474     };
2475    
2476     $Element->{$HTML_NS}->{dt} = {
2477 wakaba 1.40 %HTMLPhrasingContentChecker,
2478 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2479 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2480     %HTMLAttrStatus,
2481 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2482 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2483 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2484 wakaba 1.49 }),
2485 wakaba 1.1 };
2486    
2487     $Element->{$HTML_NS}->{dd} = {
2488 wakaba 1.72 %HTMLFlowContentChecker,
2489 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2490 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2491     %HTMLAttrStatus,
2492 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2493 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2494 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2495 wakaba 1.49 }),
2496 wakaba 1.1 };
2497    
2498     $Element->{$HTML_NS}->{a} = {
2499 wakaba 1.40 %HTMLPhrasingContentChecker,
2500 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2501 wakaba 1.40 check_attrs => sub {
2502     my ($self, $item, $element_state) = @_;
2503 wakaba 1.1 my %attr;
2504 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
2505 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
2506     $attr_ns = '' unless defined $attr_ns;
2507     my $attr_ln = $attr->manakai_local_name;
2508     my $checker;
2509 wakaba 1.73 my $status;
2510 wakaba 1.1 if ($attr_ns eq '') {
2511 wakaba 1.73 $status = {
2512     %HTMLAttrStatus,
2513 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2514 wakaba 1.73 accesskey => FEATURE_M12N10_REC,
2515     charset => FEATURE_M12N10_REC,
2516 wakaba 1.82 coords => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2517 wakaba 1.73 cryptopts => FEATURE_RFC2659,
2518     dn => FEATURE_RFC2659,
2519 wakaba 1.82 href => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2520     hreflang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2521 wakaba 1.73 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2522 wakaba 1.82 media => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED,
2523 wakaba 1.73 methods => FEATURE_HTML20_RFC,
2524     name => FEATURE_M12N10_REC_DEPRECATED,
2525     nonce => FEATURE_RFC2659,
2526     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2527     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2528     ping => FEATURE_HTML5_DEFAULT,
2529 wakaba 1.82 rel => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2530     rev => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2531 wakaba 1.73 sdapref => FEATURE_HTML20_RFC,
2532 wakaba 1.82 shape => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2533 wakaba 1.73 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2534 wakaba 1.82 target => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2535 wakaba 1.73 type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2536     urn => FEATURE_HTML20_RFC,
2537     }->{$attr_ln};
2538    
2539 wakaba 1.1 $checker = {
2540 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
2541 wakaba 1.70 ## TODO: HTML4 |charset|
2542     ## TODO: HTML4 |coords|
2543 wakaba 1.1 target => $HTMLTargetAttrChecker,
2544     href => $HTMLURIAttrChecker,
2545     ping => $HTMLSpaceURIsAttrChecker,
2546 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) },
2547 wakaba 1.71 rev => $HTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker,
2548 wakaba 1.70 ## TODO: HTML4 |shape|
2549 wakaba 1.1 media => $HTMLMQAttrChecker,
2550 wakaba 1.70 ## TODO: HTML4/XHTML1 |name|
2551 wakaba 1.1 hreflang => $HTMLLanguageTagAttrChecker,
2552     type => $HTMLIMTAttrChecker,
2553     }->{$attr_ln};
2554     if ($checker) {
2555     $attr{$attr_ln} = $attr;
2556 wakaba 1.73 } elsif ($attr_ln =~ /^data-/) {
2557     $checker = $HTMLDatasetAttrChecker;
2558     $status = $HTMLDatasetAttrStatus;
2559 wakaba 1.1 } else {
2560     $checker = $HTMLAttrChecker->{$attr_ln};
2561     }
2562     }
2563     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
2564     || $AttrChecker->{$attr_ns}->{''};
2565 wakaba 1.82 $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
2566     || $AttrStatus->{$attr_ns}->{''};
2567     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
2568 wakaba 1.62
2569 wakaba 1.1 if ($checker) {
2570 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
2571 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
2572 wakaba 1.54 #
2573 wakaba 1.1 } else {
2574     $self->{onerror}->(node => $attr, level => 'unsupported',
2575     type => 'attribute');
2576 wakaba 1.50 ## ISSUE: No conformance createria for unknown attributes in the spec
2577 wakaba 1.1 }
2578 wakaba 1.49
2579 wakaba 1.82 $self->_attr_status_info ($attr, $status);
2580 wakaba 1.1 }
2581    
2582 wakaba 1.40 $element_state->{in_a_href_original} = $self->{flag}->{in_a_href};
2583 wakaba 1.4 if (defined $attr{href}) {
2584     $self->{has_hyperlink_element} = 1;
2585 wakaba 1.40 $self->{flag}->{in_a_href} = 1;
2586 wakaba 1.4 } else {
2587 wakaba 1.1 for (qw/target ping rel media hreflang type/) {
2588     if (defined $attr{$_}) {
2589     $self->{onerror}->(node => $attr{$_},
2590     type => 'attribute not allowed');
2591     }
2592     }
2593     }
2594 wakaba 1.66
2595     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
2596 wakaba 1.1 },
2597 wakaba 1.40 check_start => sub {
2598     my ($self, $item, $element_state) = @_;
2599     $self->_add_minus_elements ($element_state, $HTMLInteractiveContent);
2600 wakaba 1.79
2601     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2602     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2603 wakaba 1.40 },
2604     check_end => sub {
2605     my ($self, $item, $element_state) = @_;
2606     $self->_remove_minus_elements ($element_state);
2607 wakaba 1.59 delete $self->{flag}->{in_a_href}
2608     unless $element_state->{in_a_href_original};
2609 wakaba 1.1
2610 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
2611 wakaba 1.1 },
2612     };
2613    
2614     $Element->{$HTML_NS}->{q} = {
2615 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2616 wakaba 1.40 %HTMLPhrasingContentChecker,
2617     check_attrs => $GetHTMLAttrsChecker->({
2618 wakaba 1.50 cite => $HTMLURIAttrChecker,
2619     }, {
2620 wakaba 1.49 %HTMLAttrStatus,
2621 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2622     cite => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2623 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2624 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
2625     sdasuff => FEATURE_HTML2X_RFC,
2626 wakaba 1.1 }),
2627 wakaba 1.66 check_start => sub {
2628     my ($self, $item, $element_state) = @_;
2629    
2630     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
2631 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2632     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2633 wakaba 1.66 },
2634 wakaba 1.1 };
2635 wakaba 1.75 ## TODO: "Quotation punctuation (such as quotation marks), if any, must be
2636     ## placed inside the <code>q</code> element." Though we cannot test the
2637     ## element against this requirement since it incluides a semantic bit,
2638     ## it might be possible to inform of the existence of quotation marks OUTSIDE
2639     ## the |q| element.
2640 wakaba 1.1
2641     $Element->{$HTML_NS}->{cite} = {
2642 wakaba 1.40 %HTMLPhrasingContentChecker,
2643 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2644 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2645     %HTMLAttrStatus,
2646 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2647 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2648 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2649 wakaba 1.49 }),
2650 wakaba 1.1 };
2651    
2652     $Element->{$HTML_NS}->{em} = {
2653 wakaba 1.40 %HTMLPhrasingContentChecker,
2654 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2655 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2656     %HTMLAttrStatus,
2657 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2658 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2659 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2660 wakaba 1.49 }),
2661 wakaba 1.1 };
2662    
2663     $Element->{$HTML_NS}->{strong} = {
2664 wakaba 1.40 %HTMLPhrasingContentChecker,
2665 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2666 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2667     %HTMLAttrStatus,
2668 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2669 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2670 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2671 wakaba 1.49 }),
2672 wakaba 1.1 };
2673    
2674     $Element->{$HTML_NS}->{small} = {
2675 wakaba 1.40 %HTMLPhrasingContentChecker,
2676 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2677     check_attrs => $GetHTMLAttrsChecker->({}, {
2678     %HTMLAttrStatus,
2679     %HTMLM12NCommonAttrStatus,
2680 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2681 wakaba 1.49 }),
2682 wakaba 1.1 };
2683    
2684 wakaba 1.51 $Element->{$HTML_NS}->{big} = {
2685     %HTMLPhrasingContentChecker,
2686     status => FEATURE_M12N10_REC,
2687     check_attrs => $GetHTMLAttrsChecker->({}, {
2688     %HTMLAttrStatus,
2689     %HTMLM12NCommonAttrStatus,
2690     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2691     }),
2692     };
2693    
2694 wakaba 1.38 $Element->{$HTML_NS}->{mark} = {
2695 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2696 wakaba 1.40 %HTMLPhrasingContentChecker,
2697 wakaba 1.1 };
2698    
2699     $Element->{$HTML_NS}->{dfn} = {
2700 wakaba 1.40 %HTMLPhrasingContentChecker,
2701 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2702 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2703     %HTMLAttrStatus,
2704 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2705 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2706 wakaba 1.49 }),
2707 wakaba 1.40 check_start => sub {
2708     my ($self, $item, $element_state) = @_;
2709     $self->_add_minus_elements ($element_state, {$HTML_NS => {dfn => 1}});
2710 wakaba 1.1
2711 wakaba 1.40 my $node = $item->{node};
2712 wakaba 1.1 my $term = $node->get_attribute_ns (undef, 'title');
2713     unless (defined $term) {
2714     for my $child (@{$node->child_nodes}) {
2715     if ($child->node_type == 1) { # ELEMENT_NODE
2716     if (defined $term) {
2717     undef $term;
2718     last;
2719     } elsif ($child->manakai_local_name eq 'abbr') {
2720     my $nsuri = $child->namespace_uri;
2721     if (defined $nsuri and $nsuri eq $HTML_NS) {
2722     my $attr = $child->get_attribute_node_ns (undef, 'title');
2723     if ($attr) {
2724     $term = $attr->value;
2725     }
2726     }
2727     }
2728     } elsif ($child->node_type == 3 or $child->node_type == 4) {
2729     ## TEXT_NODE or CDATA_SECTION_NODE
2730     if ($child->data =~ /\A[\x09-\x0D\x20]+\z/) { # Inter-element whitespace
2731     next;
2732     }
2733     undef $term;
2734     last;
2735     }
2736     }
2737     unless (defined $term) {
2738     $term = $node->text_content;
2739     }
2740     }
2741     if ($self->{term}->{$term}) {
2742     push @{$self->{term}->{$term}}, $node;
2743     } else {
2744     $self->{term}->{$term} = [$node];
2745     }
2746 wakaba 1.77 ## ISSUE: The HTML5 definition for the defined term does not work with
2747     ## |ruby| unless |dfn| has |title|.
2748 wakaba 1.79
2749     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2750     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2751 wakaba 1.40 },
2752     check_end => sub {
2753     my ($self, $item, $element_state) = @_;
2754     $self->_remove_minus_elements ($element_state);
2755 wakaba 1.1
2756 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
2757 wakaba 1.1 },
2758     };
2759    
2760     $Element->{$HTML_NS}->{abbr} = {
2761 wakaba 1.40 %HTMLPhrasingContentChecker,
2762 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2763 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2764     %HTMLAttrStatus,
2765 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2766     full => FEATURE_XHTML2_ED,
2767 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2768 wakaba 1.49 }),
2769 wakaba 1.77 ## NOTE: "If an abbreviation is pluralised, the expansion's grammatical
2770     ## number (plural vs singular) must match the grammatical number of the
2771     ## contents of the element." Though this can be checked by machine,
2772     ## it requires language-specific knowledge and dictionary, such that
2773     ## we don't support the check of the requirement.
2774     ## ISSUE: Is <abbr title="Cascading Style Sheets">CSS</abbr> conforming?
2775 wakaba 1.49 };
2776    
2777     $Element->{$HTML_NS}->{acronym} = {
2778     %HTMLPhrasingContentChecker,
2779     status => FEATURE_M12N10_REC,
2780     check_attrs => $GetHTMLAttrsChecker->({}, {
2781     %HTMLAttrStatus,
2782     %HTMLM12NCommonAttrStatus,
2783 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2784 wakaba 1.49 }),
2785 wakaba 1.1 };
2786    
2787     $Element->{$HTML_NS}->{time} = {
2788 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2789 wakaba 1.40 %HTMLPhrasingContentChecker,
2790     check_attrs => $GetHTMLAttrsChecker->({
2791 wakaba 1.1 datetime => sub { 1 }, # checked in |checker|
2792 wakaba 1.49 }, {
2793     %HTMLAttrStatus,
2794     %HTMLM12NCommonAttrStatus,
2795 wakaba 1.72 datetime => FEATURE_HTML5_FD,
2796 wakaba 1.1 }),
2797     ## TODO: Write tests
2798 wakaba 1.40 check_end => sub {
2799     my ($self, $item, $element_state) = @_;
2800 wakaba 1.1
2801 wakaba 1.40 my $attr = $item->{node}->get_attribute_node_ns (undef, 'datetime');
2802 wakaba 1.1 my $input;
2803     my $reg_sp;
2804     my $input_node;
2805     if ($attr) {
2806     $input = $attr->value;
2807     $reg_sp = qr/[\x09-\x0D\x20]*/;
2808     $input_node = $attr;
2809     } else {
2810 wakaba 1.40 $input = $item->{node}->text_content;
2811 wakaba 1.1 $reg_sp = qr/\p{Zs}*/;
2812 wakaba 1.40 $input_node = $item->{node};
2813 wakaba 1.1
2814     ## ISSUE: What is the definition for "successfully extracts a date
2815     ## or time"? If the algorithm says the string is invalid but
2816     ## return some date or time, is it "successfully"?
2817     }
2818    
2819     my $hour;
2820     my $minute;
2821     my $second;
2822     if ($input =~ /
2823     \A
2824     [\x09-\x0D\x20]*
2825     ([0-9]+) # 1
2826     (?>
2827     -([0-9]+) # 2
2828     -([0-9]+) # 3
2829     [\x09-\x0D\x20]*
2830     (?>
2831     T
2832     [\x09-\x0D\x20]*
2833     )?
2834     ([0-9]+) # 4
2835     :([0-9]+) # 5
2836     (?>
2837     :([0-9]+(?>\.[0-9]*)?|\.[0-9]*) # 6
2838     )?
2839     [\x09-\x0D\x20]*
2840     (?>
2841     Z
2842     [\x09-\x0D\x20]*
2843     |
2844     [+-]([0-9]+):([0-9]+) # 7, 8
2845     [\x09-\x0D\x20]*
2846     )?
2847     \z
2848     |
2849     :([0-9]+) # 9
2850     (?>
2851     :([0-9]+(?>\.[0-9]*)?|\.[0-9]*) # 10
2852     )?
2853     [\x09-\x0D\x20]*\z
2854     )
2855     /x) {
2856     if (defined $2) { ## YYYY-MM-DD T? hh:mm
2857     if (length $1 != 4 or length $2 != 2 or length $3 != 2 or
2858     length $4 != 2 or length $5 != 2) {
2859     $self->{onerror}->(node => $input_node,
2860     type => 'dateortime:syntax error');
2861     }
2862    
2863     if (1 <= $2 and $2 <= 12) {
2864     $self->{onerror}->(node => $input_node, type => 'datetime:bad day')
2865     if $3 < 1 or
2866     $3 > [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]->[$2];
2867     $self->{onerror}->(node => $input_node, type => 'datetime:bad day')
2868     if $2 == 2 and $3 == 29 and
2869     not ($1 % 400 == 0 or ($1 % 4 == 0 and $1 % 100 != 0));
2870     } else {
2871     $self->{onerror}->(node => $input_node,
2872     type => 'datetime:bad month');
2873     }
2874    
2875     ($hour, $minute, $second) = ($4, $5, $6);
2876    
2877     if (defined $7) { ## [+-]hh:mm
2878     if (length $7 != 2 or length $8 != 2) {
2879     $self->{onerror}->(node => $input_node,
2880     type => 'dateortime:syntax error');
2881     }
2882    
2883     $self->{onerror}->(node => $input_node,
2884     type => 'datetime:bad timezone hour')
2885     if $7 > 23;
2886     $self->{onerror}->(node => $input_node,
2887     type => 'datetime:bad timezone minute')
2888     if $8 > 59;
2889     }
2890     } else { ## hh:mm
2891     if (length $1 != 2 or length $9 != 2) {
2892     $self->{onerror}->(node => $input_node,
2893     type => qq'dateortime:syntax error');
2894     }
2895    
2896     ($hour, $minute, $second) = ($1, $9, $10);
2897     }
2898    
2899     $self->{onerror}->(node => $input_node, type => 'datetime:bad hour')
2900     if $hour > 23;
2901     $self->{onerror}->(node => $input_node, type => 'datetime:bad minute')
2902     if $minute > 59;
2903    
2904     if (defined $second) { ## s
2905     ## NOTE: Integer part of second don't have to have length of two.
2906    
2907     if (substr ($second, 0, 1) eq '.') {
2908     $self->{onerror}->(node => $input_node,
2909     type => 'dateortime:syntax error');
2910     }
2911    
2912     $self->{onerror}->(node => $input_node, type => 'datetime:bad second')
2913     if $second >= 60;
2914     }
2915     } else {
2916     $self->{onerror}->(node => $input_node,
2917     type => 'dateortime:syntax error');
2918     }
2919    
2920 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
2921 wakaba 1.1 },
2922     };
2923    
2924     $Element->{$HTML_NS}->{meter} = { ## TODO: "The recommended way of giving the value is to include it as contents of the element"
2925 wakaba 1.77 ## TODO: value inequalities (HTML5 revision 1463)
2926 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2927 wakaba 1.40 %HTMLPhrasingContentChecker,
2928     check_attrs => $GetHTMLAttrsChecker->({
2929 wakaba 1.1 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
2930     min => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
2931     low => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
2932     high => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
2933     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
2934     optimum => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
2935 wakaba 1.50 }, {
2936     %HTMLAttrStatus,
2937     high => FEATURE_HTML5_DEFAULT,
2938     low => FEATURE_HTML5_DEFAULT,
2939     max => FEATURE_HTML5_DEFAULT,
2940     min => FEATURE_HTML5_DEFAULT,
2941     optimum => FEATURE_HTML5_DEFAULT,
2942     value => FEATURE_HTML5_DEFAULT,
2943 wakaba 1.1 }),
2944     };
2945    
2946     $Element->{$HTML_NS}->{progress} = { ## TODO: recommended to use content
2947 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2948 wakaba 1.40 %HTMLPhrasingContentChecker,
2949     check_attrs => $GetHTMLAttrsChecker->({
2950 wakaba 1.1 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift >= 0 }),
2951     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift > 0 }),
2952 wakaba 1.50 }, {
2953     %HTMLAttrStatus,
2954     max => FEATURE_HTML5_DEFAULT,
2955     value => FEATURE_HTML5_DEFAULT,
2956 wakaba 1.1 }),
2957     };
2958    
2959     $Element->{$HTML_NS}->{code} = {
2960 wakaba 1.40 %HTMLPhrasingContentChecker,
2961 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2962 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2963     %HTMLAttrStatus,
2964 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2965 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2966 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2967 wakaba 1.49 }),
2968 wakaba 1.1 };
2969    
2970     $Element->{$HTML_NS}->{var} = {
2971 wakaba 1.40 %HTMLPhrasingContentChecker,
2972 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2973 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2974     %HTMLAttrStatus,
2975 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2976 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2977 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2978 wakaba 1.49 }),
2979 wakaba 1.1 };
2980    
2981     $Element->{$HTML_NS}->{samp} = {
2982 wakaba 1.40 %HTMLPhrasingContentChecker,
2983 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2984 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2985     %HTMLAttrStatus,
2986 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2987 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2988 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2989 wakaba 1.49 }),
2990 wakaba 1.1 };
2991    
2992     $Element->{$HTML_NS}->{kbd} = {
2993 wakaba 1.40 %HTMLPhrasingContentChecker,
2994 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2995 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2996     %HTMLAttrStatus,
2997 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2998 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2999 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3000 wakaba 1.49 }),
3001 wakaba 1.1 };
3002    
3003     $Element->{$HTML_NS}->{sub} = {
3004 wakaba 1.40 %HTMLPhrasingContentChecker,
3005 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3006 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3007     %HTMLAttrStatus,
3008 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3009 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3010 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
3011 wakaba 1.49 }),
3012 wakaba 1.1 };
3013    
3014 wakaba 1.51 $Element->{$HTML_NS}->{sup} = $Element->{$HTML_NS}->{sub};
3015 wakaba 1.1
3016     $Element->{$HTML_NS}->{span} = {
3017 wakaba 1.40 %HTMLPhrasingContentChecker,
3018 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3019 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3020     %HTMLAttrStatus,
3021 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3022 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
3023     dataformatas => FEATURE_HTML4_REC_RESERVED,
3024     datasrc => FEATURE_HTML4_REC_RESERVED,
3025 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3026 wakaba 1.61 sdaform => FEATURE_HTML2X_RFC,
3027 wakaba 1.49 }),
3028 wakaba 1.1 };
3029    
3030     $Element->{$HTML_NS}->{i} = {
3031 wakaba 1.40 %HTMLPhrasingContentChecker,
3032 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3033     check_attrs => $GetHTMLAttrsChecker->({}, {
3034     %HTMLAttrStatus,
3035     %HTMLM12NCommonAttrStatus,
3036 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3037 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3038 wakaba 1.49 }),
3039 wakaba 1.1 };
3040    
3041 wakaba 1.51 $Element->{$HTML_NS}->{b} = $Element->{$HTML_NS}->{i};
3042    
3043 wakaba 1.61 $Element->{$HTML_NS}->{tt} = {
3044     %HTMLPhrasingContentChecker,
3045     status => FEATURE_M12N10_REC,
3046     check_attrs => $GetHTMLAttrsChecker->({}, {
3047     %HTMLAttrStatus,
3048     %HTMLM12NCommonAttrStatus,
3049     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3050     sdaform => FEATURE_HTML20_RFC,
3051     }),
3052     };
3053 wakaba 1.51
3054     $Element->{$HTML_NS}->{s} = {
3055 wakaba 1.40 %HTMLPhrasingContentChecker,
3056 wakaba 1.51 status => FEATURE_M12N10_REC_DEPRECATED,
3057 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3058     %HTMLAttrStatus,
3059     %HTMLM12NCommonAttrStatus,
3060 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3061 wakaba 1.49 }),
3062 wakaba 1.1 };
3063    
3064 wakaba 1.51 $Element->{$HTML_NS}->{strike} = $Element->{$HTML_NS}->{s};
3065    
3066     $Element->{$HTML_NS}->{u} = $Element->{$HTML_NS}->{s};
3067    
3068 wakaba 1.1 $Element->{$HTML_NS}->{bdo} = {
3069 wakaba 1.40 %HTMLPhrasingContentChecker,
3070 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3071 wakaba 1.40 check_attrs => sub {
3072     my ($self, $item, $element_state) = @_;
3073 wakaba 1.49 $GetHTMLAttrsChecker->({}, {
3074     %HTMLAttrStatus,
3075 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3076     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3077     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3078 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3079 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3080     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3081 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
3082     sdasuff => FEATURE_HTML2X_RFC,
3083 wakaba 1.49 })->($self, $item, $element_state);
3084 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'dir')) {
3085     $self->{onerror}->(node => $item->{node},
3086     type => 'attribute missing:dir');
3087 wakaba 1.1 }
3088     },
3089     ## ISSUE: The spec does not directly say that |dir| is a enumerated attr.
3090     };
3091    
3092 wakaba 1.29 =pod
3093    
3094     ## TODO:
3095    
3096     +
3097     + <p>Partly because of the confusion described above, authors are
3098     + strongly recommended to always mark up all paragraphs with the
3099     + <code>p</code> element, and to not have any <code>ins</code> or
3100     + <code>del</code> elements that cross across any <span
3101     + title="paragraph">implied paragraphs</span>.</p>
3102     +
3103     (An informative note)
3104    
3105     <p><code>ins</code> elements should not cross <span
3106     + title="paragraph">implied paragraph</span> boundaries.</p>
3107     (normative)
3108    
3109     + <p><code>del</code> elements should not cross <span
3110     + title="paragraph">implied paragraph</span> boundaries.</p>
3111     (normative)
3112    
3113     =cut
3114    
3115 wakaba 1.1 $Element->{$HTML_NS}->{ins} = {
3116 wakaba 1.40 %HTMLTransparentChecker,
3117 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3118 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3119 wakaba 1.1 cite => $HTMLURIAttrChecker,
3120     datetime => $HTMLDatetimeAttrChecker,
3121 wakaba 1.49 }, {
3122     %HTMLAttrStatus,
3123     %HTMLM12NCommonAttrStatus,
3124 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3125 wakaba 1.72 datetime => FEATURE_HTML5_FD | FEATURE_M12N10_REC,
3126 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3127 wakaba 1.1 }),
3128 wakaba 1.66 check_start => sub {
3129     my ($self, $item, $element_state) = @_;
3130    
3131     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
3132 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3133     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3134 wakaba 1.66 },
3135 wakaba 1.1 };
3136    
3137     $Element->{$HTML_NS}->{del} = {
3138 wakaba 1.40 %HTMLTransparentChecker,
3139 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3140 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3141 wakaba 1.1 cite => $HTMLURIAttrChecker,
3142     datetime => $HTMLDatetimeAttrChecker,
3143 wakaba 1.49 }, {
3144     %HTMLAttrStatus,
3145     %HTMLM12NCommonAttrStatus,
3146 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3147 wakaba 1.72 datetime => FEATURE_HTML5_FD | FEATURE_M12N10_REC,
3148 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3149 wakaba 1.1 }),
3150 wakaba 1.40 check_end => sub {
3151     my ($self, $item, $element_state) = @_;
3152     if ($element_state->{has_significant}) {
3153     ## NOTE: Significantness flag does not propagate.
3154     } elsif ($item->{transparent}) {
3155     #
3156     } else {
3157     $self->{onerror}->(node => $item->{node},
3158     level => $self->{should_level},
3159     type => 'no significant content');
3160     }
3161 wakaba 1.1 },
3162 wakaba 1.66 check_start => sub {
3163     my ($self, $item, $element_state) = @_;
3164    
3165     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
3166 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3167     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3168 wakaba 1.66 },
3169 wakaba 1.1 };
3170    
3171 wakaba 1.35 $Element->{$HTML_NS}->{figure} = {
3172 wakaba 1.72 %HTMLFlowContentChecker,
3173 wakaba 1.48 status => FEATURE_HTML5_FD,
3174 wakaba 1.72 ## NOTE: legend, Flow | Flow, legend?
3175 wakaba 1.41 check_child_element => sub {
3176     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3177     $child_is_transparent, $element_state) = @_;
3178     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3179     $self->{onerror}->(node => $child_el,
3180     type => 'element not allowed:minus',
3181     level => $self->{must_level});
3182     $element_state->{has_non_legend} = 1;
3183     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3184     #
3185     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') {
3186     if ($element_state->{has_legend_at_first}) {
3187     $self->{onerror}->(node => $child_el,
3188     type => 'element not allowed:figure legend',
3189     level => $self->{must_level});
3190     } elsif ($element_state->{has_legend}) {
3191     $self->{onerror}->(node => $element_state->{has_legend},
3192     type => 'element not allowed:figure legend',
3193     level => $self->{must_level});
3194     $element_state->{has_legend} = $child_el;
3195     } elsif ($element_state->{has_non_legend}) {
3196     $element_state->{has_legend} = $child_el;
3197     } else {
3198     $element_state->{has_legend_at_first} = 1;
3199 wakaba 1.35 }
3200 wakaba 1.41 delete $element_state->{has_non_legend};
3201     } else {
3202 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
3203 wakaba 1.43 $element_state->{has_non_legend} = 1 unless $child_is_transparent;
3204 wakaba 1.41 }
3205     },
3206     check_child_text => sub {
3207     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3208     if ($has_significant) {
3209     $element_state->{has_non_legend} = 1;
3210 wakaba 1.35 }
3211 wakaba 1.41 },
3212     check_end => sub {
3213     my ($self, $item, $element_state) = @_;
3214 wakaba 1.35
3215 wakaba 1.41 if ($element_state->{has_legend_at_first}) {
3216     #
3217     } elsif ($element_state->{has_legend}) {
3218     if ($element_state->{has_non_legend}) {
3219     $self->{onerror}->(node => $element_state->{has_legend},
3220 wakaba 1.35 type => 'element not allowed:figure legend',
3221     level => $self->{must_level});
3222     }
3223     }
3224 wakaba 1.41
3225 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
3226 wakaba 1.41 ## ISSUE: |<figure><legend>aa</legend></figure>| should be an error?
3227 wakaba 1.35 },
3228     };
3229 wakaba 1.8 ## TODO: Test for <nest/> in <figure/>
3230 wakaba 1.1
3231     $Element->{$HTML_NS}->{img} = {
3232 wakaba 1.40 %HTMLEmptyChecker,
3233 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3234 wakaba 1.40 check_attrs => sub {
3235     my ($self, $item, $element_state) = @_;
3236 wakaba 1.1 $GetHTMLAttrsChecker->({
3237 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
3238     bottom => 1, middle => 1, top => 1, left => 1, right => 1,
3239     }),
3240 wakaba 1.1 alt => sub { }, ## NOTE: No syntactical requirement
3241 wakaba 1.70 border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3242 wakaba 1.1 src => $HTMLURIAttrChecker,
3243     usemap => $HTMLUsemapAttrChecker,
3244 wakaba 1.70 hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3245 wakaba 1.1 ismap => sub {
3246 wakaba 1.40 my ($self, $attr, $parent_item) = @_;
3247     if (not $self->{flag}->{in_a_href}) {
3248 wakaba 1.15 $self->{onerror}->(node => $attr,
3249 wakaba 1.59 type => 'attribute not allowed:ismap',
3250     level => $self->{must_level});
3251 wakaba 1.1 }
3252 wakaba 1.40 $GetHTMLBooleanAttrChecker->('ismap')->($self, $attr, $parent_item);
3253 wakaba 1.1 },
3254 wakaba 1.70 longdesc => $HTMLURIAttrChecker,
3255     ## TODO: HTML4 |name|
3256 wakaba 1.1 ## TODO: height
3257 wakaba 1.70 vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3258 wakaba 1.1 ## TODO: width
3259 wakaba 1.49 }, {
3260     %HTMLAttrStatus,
3261 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3262 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
3263 wakaba 1.50 alt => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3264 wakaba 1.49 border => FEATURE_M12N10_REC_DEPRECATED,
3265 wakaba 1.50 height => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3266 wakaba 1.49 hspace => FEATURE_M12N10_REC_DEPRECATED,
3267 wakaba 1.82 ismap => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3268 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3269 wakaba 1.49 longdesc => FEATURE_M12N10_REC,
3270     name => FEATURE_M12N10_REC_DEPRECATED,
3271 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
3272 wakaba 1.82 src => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3273     usemap => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3274 wakaba 1.49 vspace => FEATURE_M12N10_REC_DEPRECATED,
3275 wakaba 1.50 width => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3276 wakaba 1.66 })->($self, $item, $element_state);
3277 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'alt')) {
3278     $self->{onerror}->(node => $item->{node},
3279 wakaba 1.37 type => 'attribute missing:alt',
3280     level => $self->{should_level});
3281 wakaba 1.1 }
3282 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'src')) {
3283     $self->{onerror}->(node => $item->{node},
3284     type => 'attribute missing:src');
3285 wakaba 1.1 }
3286 wakaba 1.66
3287     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
3288     $element_state->{uri_info}->{lowsrc}->{type}->{embedded} = 1;
3289     $element_state->{uri_info}->{dynsrc}->{type}->{embedded} = 1;
3290     $element_state->{uri_info}->{longdesc}->{type}->{cite} = 1;
3291 wakaba 1.1 },
3292     };
3293    
3294     $Element->{$HTML_NS}->{iframe} = {
3295 wakaba 1.40 %HTMLTextChecker,
3296 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3297     ## NOTE: Not part of M12N10 Strict
3298 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3299 wakaba 1.76 name => $HTMLBrowsingContextNameAttrChecker,
3300 wakaba 1.1 src => $HTMLURIAttrChecker,
3301 wakaba 1.49 }, {
3302     %HTMLAttrStatus,
3303     %HTMLM12NCommonAttrStatus,
3304     align => FEATURE_XHTML10_REC,
3305 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3306 wakaba 1.49 frameborder => FEATURE_M12N10_REC,
3307     height => FEATURE_M12N10_REC,
3308 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3309 wakaba 1.49 longdesc => FEATURE_M12N10_REC,
3310     marginheight => FEATURE_M12N10_REC,
3311     marginwidth => FEATURE_M12N10_REC,
3312 wakaba 1.76 #name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
3313     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3314 wakaba 1.49 scrolling => FEATURE_M12N10_REC,
3315 wakaba 1.50 src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3316     title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3317 wakaba 1.49 width => FEATURE_M12N10_REC,
3318 wakaba 1.1 }),
3319 wakaba 1.66 check_start => sub {
3320     my ($self, $item, $element_state) = @_;
3321    
3322     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
3323 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3324     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3325 wakaba 1.66 },
3326 wakaba 1.40 };
3327    
3328 wakaba 1.1 $Element->{$HTML_NS}->{embed} = {
3329 wakaba 1.40 %HTMLEmptyChecker,
3330 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3331 wakaba 1.40 check_attrs => sub {
3332     my ($self, $item, $element_state) = @_;
3333 wakaba 1.1 my $has_src;
3334 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
3335 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
3336     $attr_ns = '' unless defined $attr_ns;
3337     my $attr_ln = $attr->manakai_local_name;
3338     my $checker;
3339 wakaba 1.73
3340     my $status = {
3341     %HTMLAttrStatus,
3342     height => FEATURE_HTML5_DEFAULT,
3343     src => FEATURE_HTML5_DEFAULT,
3344     type => FEATURE_HTML5_DEFAULT,
3345     width => FEATURE_HTML5_DEFAULT,
3346     }->{$attr_ln};
3347    
3348 wakaba 1.1 if ($attr_ns eq '') {
3349     if ($attr_ln eq 'src') {
3350     $checker = $HTMLURIAttrChecker;
3351     $has_src = 1;
3352     } elsif ($attr_ln eq 'type') {
3353     $checker = $HTMLIMTAttrChecker;
3354 wakaba 1.73 } elsif ($attr_ln =~ /^data-/) {
3355     $checker = $HTMLDatasetAttrChecker;
3356     $status = $HTMLDatasetAttrStatus;
3357 wakaba 1.1 } else {
3358     ## TODO: height
3359     ## TODO: width
3360     $checker = $HTMLAttrChecker->{$attr_ln}
3361     || sub { }; ## NOTE: Any local attribute is ok.
3362 wakaba 1.82 $status = FEATURE_HTML5_DEFAULT | FEATURE_ALLOWED;
3363 wakaba 1.1 }
3364     }
3365     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
3366 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
3367     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
3368     || $AttrStatus->{$attr_ns}->{''};
3369     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
3370 wakaba 1.62
3371 wakaba 1.1 if ($checker) {
3372 wakaba 1.66 $checker->($self, $attr, $item, $element_state);
3373 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
3374 wakaba 1.54 #
3375 wakaba 1.1 } else {
3376     $self->{onerror}->(node => $attr, level => 'unsupported',
3377     type => 'attribute');
3378 wakaba 1.50 ## ISSUE: No conformance createria for global attributes in the spec
3379     }
3380    
3381 wakaba 1.82 $self->_attr_status_info ($attr, $status);
3382 wakaba 1.1 }
3383    
3384     unless ($has_src) {
3385 wakaba 1.40 $self->{onerror}->(node => $item->{node},
3386 wakaba 1.1 type => 'attribute missing:src');
3387     }
3388 wakaba 1.66
3389     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
3390 wakaba 1.1 },
3391     };
3392    
3393 wakaba 1.49 ## TODO:
3394     ## {applet} FEATURE_M12N10_REC_DEPRECATED
3395     ## class, id, title, alt, archive, code, codebase, height, object, width name style,hspace,vspace(xhtml10)
3396    
3397 wakaba 1.1 $Element->{$HTML_NS}->{object} = {
3398 wakaba 1.40 %HTMLTransparentChecker,
3399 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3400 wakaba 1.40 check_attrs => sub {
3401     my ($self, $item, $element_state) = @_;
3402 wakaba 1.1 $GetHTMLAttrsChecker->({
3403 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
3404     bottom => 1, middle => 1, top => 1, left => 1, right => 1,
3405     }),
3406     archive => $HTMLSpaceURIsAttrChecker,
3407     ## TODO: Relative to @codebase
3408     border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3409     classid => $HTMLURIAttrChecker,
3410     codebase => $HTMLURIAttrChecker,
3411     codetype => $HTMLIMTAttrChecker,
3412     ## TODO: "RECOMMENDED when |classid| is specified" [HTML4]
3413 wakaba 1.1 data => $HTMLURIAttrChecker,
3414 wakaba 1.70 declare => $GetHTMLBooleanAttrChecker->('declare'),
3415     ## NOTE: "The object MUST be instantiated by a subsequent OBJECT ..."
3416     ## [HTML4] but we don't know how to test this.
3417     hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3418 wakaba 1.76 name => $HTMLBrowsingContextNameAttrChecker,
3419 wakaba 1.70 standby => sub {}, ## NOTE: %Text; in HTML4
3420 wakaba 1.1 type => $HTMLIMTAttrChecker,
3421     usemap => $HTMLUsemapAttrChecker,
3422 wakaba 1.70 vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3423 wakaba 1.1 ## TODO: width
3424     ## TODO: height
3425 wakaba 1.49 }, {
3426     %HTMLAttrStatus,
3427 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3428 wakaba 1.49 align => FEATURE_XHTML10_REC,
3429 wakaba 1.82 archive => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3430 wakaba 1.49 border => FEATURE_XHTML10_REC,
3431     classid => FEATURE_M12N10_REC,
3432     codebase => FEATURE_M12N10_REC,
3433     codetype => FEATURE_M12N10_REC,
3434 wakaba 1.82 'content-length' => FEATURE_XHTML2_ED,
3435 wakaba 1.50 data => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3436 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
3437     dataformatas => FEATURE_HTML4_REC_RESERVED,
3438     datasrc => FEATURE_HTML4_REC_RESERVED,
3439 wakaba 1.82 declare => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3440 wakaba 1.50 height => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3441 wakaba 1.49 hspace => FEATURE_XHTML10_REC,
3442 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3443 wakaba 1.76 name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3444 wakaba 1.49 standby => FEATURE_M12N10_REC,
3445 wakaba 1.50 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3446     type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3447     usemap => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3448 wakaba 1.49 vspace => FEATURE_XHTML10_REC,
3449 wakaba 1.50 width => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3450 wakaba 1.66 })->($self, $item, $element_state);
3451 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'data')) {
3452     unless ($item->{node}->has_attribute_ns (undef, 'type')) {
3453     $self->{onerror}->(node => $item->{node},
3454 wakaba 1.1 type => 'attribute missing:data|type');
3455     }
3456     }
3457 wakaba 1.66
3458     $element_state->{uri_info}->{data}->{type}->{embedded} = 1;
3459     $element_state->{uri_info}->{classid}->{type}->{embedded} = 1;
3460     $element_state->{uri_info}->{codebase}->{type}->{base} = 1;
3461     ## TODO: archive
3462     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
3463 wakaba 1.1 },
3464 wakaba 1.72 ## NOTE: param*, transparent (Flow)
3465 wakaba 1.41 check_child_element => sub {
3466     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3467     $child_is_transparent, $element_state) = @_;
3468     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3469     $self->{onerror}->(node => $child_el,
3470     type => 'element not allowed:minus',
3471     level => $self->{must_level});
3472     $element_state->{has_non_legend} = 1;
3473     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3474     #
3475     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'param') {
3476     if ($element_state->{has_non_param}) {
3477 wakaba 1.72 $self->{onerror}->(node => $child_el, ## TODO: type
3478     type => 'element not allowed:flow',
3479 wakaba 1.41 level => $self->{must_level});
3480 wakaba 1.39 }
3481 wakaba 1.41 } else {
3482 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
3483 wakaba 1.41 $element_state->{has_non_param} = 1;
3484 wakaba 1.39 }
3485 wakaba 1.25 },
3486 wakaba 1.41 check_child_text => sub {
3487     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3488     if ($has_significant) {
3489     $element_state->{has_non_param} = 1;
3490     }
3491 wakaba 1.42 },
3492     check_end => sub {
3493     my ($self, $item, $element_state) = @_;
3494     if ($element_state->{has_significant}) {
3495 wakaba 1.46 $item->{real_parent_state}->{has_significant} = 1;
3496 wakaba 1.42 } elsif ($item->{node}->manakai_parent_element) {
3497     ## NOTE: Transparent.
3498     } else {
3499     $self->{onerror}->(node => $item->{node},
3500     level => $self->{should_level},
3501     type => 'no significant content');
3502     }
3503     },
3504 wakaba 1.8 ## TODO: Tests for <nest/> in <object/>
3505 wakaba 1.1 };
3506 wakaba 1.41 ## ISSUE: Is |<menu><object data><li>aa</li></object></menu>| conforming?
3507     ## What about |<section><object data><style scoped></style>x</object></section>|?
3508     ## |<section><ins></ins><object data><style scoped></style>x</object></section>|?
3509 wakaba 1.1
3510     $Element->{$HTML_NS}->{param} = {
3511 wakaba 1.40 %HTMLEmptyChecker,
3512 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3513 wakaba 1.40 check_attrs => sub {
3514     my ($self, $item, $element_state) = @_;
3515 wakaba 1.1 $GetHTMLAttrsChecker->({
3516     name => sub { },
3517 wakaba 1.70 type => $HTMLIMTAttrChecker,
3518 wakaba 1.1 value => sub { },
3519 wakaba 1.70 valuetype => $GetHTMLEnumeratedAttrChecker->({
3520     data => 1, ref => 1, object => 1,
3521     }),
3522 wakaba 1.49 }, {
3523     %HTMLAttrStatus,
3524 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3525 wakaba 1.82 name => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3526     type => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3527     value => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3528     valuetype => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3529 wakaba 1.66 })->(@_);
3530 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'name')) {
3531     $self->{onerror}->(node => $item->{node},
3532 wakaba 1.1 type => 'attribute missing:name');
3533     }
3534 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'value')) {
3535     $self->{onerror}->(node => $item->{node},
3536 wakaba 1.1 type => 'attribute missing:value');
3537     }
3538     },
3539     };
3540    
3541     $Element->{$HTML_NS}->{video} = {
3542 wakaba 1.40 %HTMLTransparentChecker,
3543 wakaba 1.48 status => FEATURE_HTML5_LC,
3544 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3545 wakaba 1.1 src => $HTMLURIAttrChecker,
3546     ## TODO: start, loopstart, loopend, end
3547     ## ISSUE: they MUST be "value time offset"s. Value?
3548 wakaba 1.11 ## ISSUE: playcount has no conformance creteria
3549 wakaba 1.1 autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
3550     controls => $GetHTMLBooleanAttrChecker->('controls'),
3551 wakaba 1.59 poster => $HTMLURIAttrChecker,
3552 wakaba 1.42 ## TODO: width, height
3553 wakaba 1.50 }, {
3554     %HTMLAttrStatus,
3555     autoplay => FEATURE_HTML5_LC,
3556     controls => FEATURE_HTML5_LC,
3557     end => FEATURE_HTML5_LC,
3558     height => FEATURE_HTML5_LC,
3559     loopend => FEATURE_HTML5_LC,
3560     loopstart => FEATURE_HTML5_LC,
3561     playcount => FEATURE_HTML5_LC,
3562     poster => FEATURE_HTML5_LC,
3563     src => FEATURE_HTML5_LC,
3564     start => FEATURE_HTML5_LC,
3565     width => FEATURE_HTML5_LC,
3566 wakaba 1.1 }),
3567 wakaba 1.42 check_start => sub {
3568     my ($self, $item, $element_state) = @_;
3569     $element_state->{allow_source}
3570     = not $item->{node}->has_attribute_ns (undef, 'src');
3571     $element_state->{has_source} ||= $element_state->{allow_source} * -1;
3572     ## NOTE: It might be set true by |check_element|.
3573 wakaba 1.66
3574     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
3575     $element_state->{uri_info}->{poster}->{type}->{embedded} = 1;
3576 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3577     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3578 wakaba 1.42 },
3579     check_child_element => sub {
3580     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3581     $child_is_transparent, $element_state) = @_;
3582     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3583     $self->{onerror}->(node => $child_el,
3584     type => 'element not allowed:minus',
3585     level => $self->{must_level});
3586     delete $element_state->{allow_source};
3587     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3588     #
3589     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'source') {
3590 wakaba 1.45 unless ($element_state->{allow_source}) {
3591 wakaba 1.72 $self->{onerror}->(node => $child_el, ## TODO: type
3592     type => 'element not allowed:flow',
3593 wakaba 1.42 level => $self->{must_level});
3594     }
3595 wakaba 1.45 $element_state->{has_source} = 1;
3596 wakaba 1.1 } else {
3597 wakaba 1.42 delete $element_state->{allow_source};
3598 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
3599 wakaba 1.42 }
3600     },
3601     check_child_text => sub {
3602     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3603     if ($has_significant) {
3604     delete $element_state->{allow_source};
3605     }
3606 wakaba 1.72 $HTMLFlowContentChecker{check_child_text}->(@_);
3607 wakaba 1.42 },
3608     check_end => sub {
3609     my ($self, $item, $element_state) = @_;
3610     if ($element_state->{has_source} == -1) {
3611     $self->{onerror}->(node => $item->{node},
3612     type => 'element missing:source',
3613     level => $self->{must_level});
3614 wakaba 1.1 }
3615 wakaba 1.42
3616     $Element->{$HTML_NS}->{object}->{check_end}->(@_);
3617 wakaba 1.1 },
3618     };
3619    
3620     $Element->{$HTML_NS}->{audio} = {
3621 wakaba 1.40 %{$Element->{$HTML_NS}->{video}},
3622 wakaba 1.48 status => FEATURE_HTML5_LC,
3623 wakaba 1.42 check_attrs => $GetHTMLAttrsChecker->({
3624     src => $HTMLURIAttrChecker,
3625     ## TODO: start, loopstart, loopend, end
3626     ## ISSUE: they MUST be "value time offset"s. Value?
3627     ## ISSUE: playcount has no conformance creteria
3628     autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
3629     controls => $GetHTMLBooleanAttrChecker->('controls'),
3630 wakaba 1.50 }, {
3631     %HTMLAttrStatus,
3632     autoplay => FEATURE_HTML5_LC,
3633     controls => FEATURE_HTML5_LC,
3634     end => FEATURE_HTML5_LC,
3635     loopend => FEATURE_HTML5_LC,
3636     loopstart => FEATURE_HTML5_LC,
3637     playcount => FEATURE_HTML5_LC,
3638     src => FEATURE_HTML5_LC,
3639     start => FEATURE_HTML5_LC,
3640 wakaba 1.42 }),
3641 wakaba 1.1 };
3642    
3643     $Element->{$HTML_NS}->{source} = {
3644 wakaba 1.40 %HTMLEmptyChecker,
3645 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3646 wakaba 1.40 check_attrs => sub {
3647     my ($self, $item, $element_state) = @_;
3648 wakaba 1.1 $GetHTMLAttrsChecker->({
3649     src => $HTMLURIAttrChecker,
3650     type => $HTMLIMTAttrChecker,
3651     media => $HTMLMQAttrChecker,
3652 wakaba 1.50 }, {
3653     %HTMLAttrStatus,
3654     media => FEATURE_HTML5_DEFAULT,
3655     src => FEATURE_HTML5_DEFAULT,
3656     type => FEATURE_HTML5_DEFAULT,
3657 wakaba 1.66 })->(@_);
3658 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'src')) {
3659     $self->{onerror}->(node => $item->{node},
3660 wakaba 1.1 type => 'attribute missing:src');
3661     }
3662 wakaba 1.66
3663     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
3664 wakaba 1.1 },
3665     };
3666    
3667     $Element->{$HTML_NS}->{canvas} = {
3668 wakaba 1.40 %HTMLTransparentChecker,
3669 wakaba 1.48 status => FEATURE_HTML5_LC,
3670 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3671 wakaba 1.1 height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3672     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3673 wakaba 1.50 }, {
3674     %HTMLAttrStatus,
3675     height => FEATURE_HTML5_LC,
3676     width => FEATURE_HTML5_LC,
3677 wakaba 1.1 }),
3678     };
3679    
3680     $Element->{$HTML_NS}->{map} = {
3681 wakaba 1.72 %HTMLFlowContentChecker,
3682 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3683 wakaba 1.40 check_attrs => sub {
3684     my ($self, $item, $element_state) = @_;
3685 wakaba 1.4 my $has_id;
3686     $GetHTMLAttrsChecker->({
3687     id => sub {
3688     ## NOTE: same as global |id=""|, with |$self->{map}| registeration
3689     my ($self, $attr) = @_;
3690     my $value = $attr->value;
3691     if (length $value > 0) {
3692     if ($self->{id}->{$value}) {
3693     $self->{onerror}->(node => $attr, type => 'duplicate ID');
3694     push @{$self->{id}->{$value}}, $attr;
3695     } else {
3696     $self->{id}->{$value} = [$attr];
3697     }
3698 wakaba 1.1 } else {
3699 wakaba 1.4 ## NOTE: MUST contain at least one character
3700     $self->{onerror}->(node => $attr, type => 'empty attribute value');
3701 wakaba 1.1 }
3702 wakaba 1.4 if ($value =~ /[\x09-\x0D\x20]/) {
3703     $self->{onerror}->(node => $attr, type => 'space in ID');
3704     }
3705     $self->{map}->{$value} ||= $attr;
3706     $has_id = 1;
3707     },
3708 wakaba 1.70 ## TODO: HTML4 |name|
3709 wakaba 1.49 }, {
3710     %HTMLAttrStatus,
3711 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3712     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3713     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3714     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3715 wakaba 1.49 name => FEATURE_M12N10_REC_DEPRECATED,
3716 wakaba 1.50 onclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3717     ondblclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3718     onmousedown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3719     onmouseup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3720     onmouseover => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3721     onmousemove => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3722     onmouseout => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3723     onkeypress => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3724     onkeydown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3725     onkeyup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3726     title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3727 wakaba 1.66 })->(@_);
3728 wakaba 1.40 $self->{onerror}->(node => $item->{node}, type => 'attribute missing:id')
3729 wakaba 1.4 unless $has_id;
3730     },
3731 wakaba 1.59 check_start => sub {
3732     my ($self, $item, $element_state) = @_;
3733     $element_state->{in_map_original} = $self->{flag}->{in_map};
3734     $self->{flag}->{in_map} = 1;
3735 wakaba 1.79
3736     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3737     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3738 wakaba 1.59 },
3739     check_end => sub {
3740     my ($self, $item, $element_state) = @_;
3741     delete $self->{flag}->{in_map} unless $element_state->{in_map_original};
3742 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
3743 wakaba 1.59 },
3744 wakaba 1.1 };
3745    
3746     $Element->{$HTML_NS}->{area} = {
3747 wakaba 1.40 %HTMLEmptyChecker,
3748 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3749 wakaba 1.40 check_attrs => sub {
3750     my ($self, $item, $element_state) = @_;
3751 wakaba 1.1 my %attr;
3752     my $coords;
3753 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
3754 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
3755     $attr_ns = '' unless defined $attr_ns;
3756     my $attr_ln = $attr->manakai_local_name;
3757     my $checker;
3758 wakaba 1.73 my $status;
3759 wakaba 1.1 if ($attr_ns eq '') {
3760 wakaba 1.73 $status = {
3761     %HTMLAttrStatus,
3762     %HTMLM12NCommonAttrStatus,
3763     accesskey => FEATURE_M12N10_REC,
3764     alt => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3765     coords => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3766     href => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3767     hreflang => FEATURE_HTML5_DEFAULT,
3768     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3769     media => FEATURE_HTML5_DEFAULT,
3770     nohref => FEATURE_M12N10_REC,
3771     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3772     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3773     ping => FEATURE_HTML5_DEFAULT,
3774     rel => FEATURE_HTML5_DEFAULT,
3775     shape => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3776     tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3777     target => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3778     type => FEATURE_HTML5_DEFAULT,
3779     }->{$attr_ln};
3780    
3781 wakaba 1.1 $checker = {
3782 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
3783 wakaba 1.1 alt => sub { },
3784     ## NOTE: |alt| value has no conformance creteria.
3785     shape => $GetHTMLEnumeratedAttrChecker->({
3786     circ => -1, circle => 1,
3787     default => 1,
3788     poly => 1, polygon => -1,
3789     rect => 1, rectangle => -1,
3790     }),
3791     coords => sub {
3792     my ($self, $attr) = @_;
3793     my $value = $attr->value;
3794     if ($value =~ /\A-?[0-9]+(?>,-?[0-9]+)*\z/) {
3795     $coords = [split /,/, $value];
3796     } else {
3797     $self->{onerror}->(node => $attr,
3798     type => 'coords:syntax error');
3799     }
3800     },
3801 wakaba 1.70 nohref => $GetHTMLBooleanAttrChecker->('nohref'),
3802     target => $HTMLTargetAttrChecker,
3803 wakaba 1.1 href => $HTMLURIAttrChecker,
3804     ping => $HTMLSpaceURIsAttrChecker,
3805 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) },
3806 wakaba 1.1 media => $HTMLMQAttrChecker,
3807     hreflang => $HTMLLanguageTagAttrChecker,
3808     type => $HTMLIMTAttrChecker,
3809     }->{$attr_ln};
3810     if ($checker) {
3811     $attr{$attr_ln} = $attr;
3812 wakaba 1.73 } elsif ($attr_ln =~ /^data-/) {
3813     $checker = $HTMLDatasetAttrChecker;
3814     $status = $HTMLDatasetAttrStatus;
3815 wakaba 1.1 } else {
3816     $checker = $HTMLAttrChecker->{$attr_ln};
3817     }
3818     }
3819     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
3820 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
3821     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
3822     || $AttrStatus->{$attr_ns}->{''};
3823     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
3824 wakaba 1.62
3825 wakaba 1.1 if ($checker) {
3826 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
3827 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
3828 wakaba 1.54 #
3829 wakaba 1.1 } else {
3830     $self->{onerror}->(node => $attr, level => 'unsupported',
3831     type => 'attribute');
3832     ## ISSUE: No comformance createria for unknown attributes in the spec
3833     }
3834 wakaba 1.49
3835 wakaba 1.82 $self->_attr_status_info ($attr, $status);
3836 wakaba 1.1 }
3837    
3838     if (defined $attr{href}) {
3839 wakaba 1.4 $self->{has_hyperlink_element} = 1;
3840 wakaba 1.1 unless (defined $attr{alt}) {
3841 wakaba 1.40 $self->{onerror}->(node => $item->{node},
3842 wakaba 1.1 type => 'attribute missing:alt');
3843     }
3844     } else {
3845     for (qw/target ping rel media hreflang type alt/) {
3846     if (defined $attr{$_}) {
3847     $self->{onerror}->(node => $attr{$_},
3848     type => 'attribute not allowed');
3849     }
3850     }
3851     }
3852    
3853     my $shape = 'rectangle';
3854     if (defined $attr{shape}) {
3855     $shape = {
3856     circ => 'circle', circle => 'circle',
3857     default => 'default',
3858     poly => 'polygon', polygon => 'polygon',
3859     rect => 'rectangle', rectangle => 'rectangle',
3860     }->{lc $attr{shape}->value} || 'rectangle';
3861     ## TODO: ASCII lowercase?
3862     }
3863    
3864     if ($shape eq 'circle') {
3865     if (defined $attr{coords}) {
3866     if (defined $coords) {
3867     if (@$coords == 3) {
3868     if ($coords->[2] < 0) {
3869     $self->{onerror}->(node => $attr{coords},
3870     type => 'coords:out of range:2');
3871     }
3872     } else {
3873     $self->{onerror}->(node => $attr{coords},
3874     type => 'coords:number:3:'.@$coords);
3875     }
3876     } else {
3877     ## NOTE: A syntax error has been reported.
3878     }
3879     } else {
3880 wakaba 1.40 $self->{onerror}->(node => $item->{node},
3881 wakaba 1.1 type => 'attribute missing:coords');
3882     }
3883     } elsif ($shape eq 'default') {
3884     if (defined $attr{coords}) {
3885     $self->{onerror}->(node => $attr{coords},
3886     type => 'attribute not allowed');
3887     }
3888     } elsif ($shape eq 'polygon') {
3889     if (defined $attr{coords}) {
3890     if (defined $coords) {
3891     if (@$coords >= 6) {
3892     unless (@$coords % 2 == 0) {
3893     $self->{onerror}->(node => $attr{coords},
3894     type => 'coords:number:even:'.@$coords);
3895     }
3896     } else {
3897     $self->{onerror}->(node => $attr{coords},
3898     type => 'coords:number:>=6:'.@$coords);
3899     }
3900     } else {
3901     ## NOTE: A syntax error has been reported.
3902     }
3903     } else {
3904 wakaba 1.40 $self->{onerror}->(node => $item->{node},
3905 wakaba 1.1 type => 'attribute missing:coords');
3906     }
3907     } elsif ($shape eq 'rectangle') {
3908     if (defined $attr{coords}) {
3909     if (defined $coords) {
3910     if (@$coords == 4) {
3911     unless ($coords->[0] < $coords->[2]) {
3912     $self->{onerror}->(node => $attr{coords},
3913     type => 'coords:out of range:0');
3914     }
3915     unless ($coords->[1] < $coords->[3]) {
3916     $self->{onerror}->(node => $attr{coords},
3917     type => 'coords:out of range:1');
3918     }
3919     } else {
3920     $self->{onerror}->(node => $attr{coords},
3921     type => 'coords:number:4:'.@$coords);
3922     }
3923     } else {
3924     ## NOTE: A syntax error has been reported.
3925     }
3926     } else {
3927 wakaba 1.40 $self->{onerror}->(node => $item->{node},
3928 wakaba 1.1 type => 'attribute missing:coords');
3929     }
3930     }
3931 wakaba 1.66
3932     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
3933 wakaba 1.1 },
3934 wakaba 1.59 check_start => sub {
3935     my ($self, $item, $element_state) = @_;
3936     unless ($self->{flag}->{in_map} or
3937     not $item->{node}->manakai_parent_element) {
3938     $self->{onerror}->(node => $item->{node},
3939     type => 'element not allowed:area',
3940     level => $self->{must_level});
3941     }
3942 wakaba 1.79
3943     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3944     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3945 wakaba 1.59 },
3946 wakaba 1.1 };
3947    
3948     $Element->{$HTML_NS}->{table} = {
3949 wakaba 1.40 %HTMLChecker,
3950 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3951 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
3952 wakaba 1.70 ## TODO: HTML4 |cellspacing|, |cellpadding|
3953 wakaba 1.69 frame => $GetHTMLEnumeratedAttrChecker->({
3954     void => 1, above => 1, below => 1, hsides => 1, vsides => 1,
3955     lhs => 1, rhs => 1, box => 1, border => 1,
3956     }),
3957     rules => $GetHTMLEnumeratedAttrChecker->({
3958     none => 1, groups => 1, rows => 1, cols => 1, all => 1,
3959     }),
3960     summary => sub {}, ## NOTE: %Text; in HTML4.
3961     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## %Pixels;
3962     }, {
3963 wakaba 1.49 %HTMLAttrStatus,
3964 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3965 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
3966     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
3967     border => FEATURE_M12N10_REC,
3968     cellpadding => FEATURE_M12N10_REC,
3969     cellspacing => FEATURE_M12N10_REC,
3970 wakaba 1.61 cols => FEATURE_RFC1942,
3971 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
3972     dataformatas => FEATURE_HTML4_REC_RESERVED,
3973     datapagesize => FEATURE_M12N10_REC,
3974     datasrc => FEATURE_HTML4_REC_RESERVED,
3975     frame => FEATURE_M12N10_REC,
3976 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3977 wakaba 1.49 rules => FEATURE_M12N10_REC,
3978     summary => FEATURE_M12N10_REC,
3979     width => FEATURE_M12N10_REC,
3980     }),
3981 wakaba 1.40 check_start => sub {
3982     my ($self, $item, $element_state) = @_;
3983     $element_state->{phase} = 'before caption';
3984 wakaba 1.66
3985     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
3986 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3987     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3988 wakaba 1.40 },
3989     check_child_element => sub {
3990     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3991     $child_is_transparent, $element_state) = @_;
3992     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3993     $self->{onerror}->(node => $child_el,
3994     type => 'element not allowed:minus',
3995     level => $self->{must_level});
3996     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3997     #
3998     } elsif ($element_state->{phase} eq 'in tbodys') {
3999     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4000     #$element_state->{phase} = 'in tbodys';
4001     } elsif (not $element_state->{has_tfoot} and
4002     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4003     $element_state->{phase} = 'after tfoot';
4004     $element_state->{has_tfoot} = 1;
4005     } else {
4006     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4007     }
4008     } elsif ($element_state->{phase} eq 'in trs') {
4009     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4010     #$element_state->{phase} = 'in trs';
4011     } elsif (not $element_state->{has_tfoot} and
4012     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4013     $element_state->{phase} = 'after tfoot';
4014     $element_state->{has_tfoot} = 1;
4015     } else {
4016     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4017     }
4018     } elsif ($element_state->{phase} eq 'after thead') {
4019     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4020     $element_state->{phase} = 'in tbodys';
4021     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4022     $element_state->{phase} = 'in trs';
4023     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4024     $element_state->{phase} = 'in tbodys';
4025     $element_state->{has_tfoot} = 1;
4026     } else {
4027     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4028     }
4029     } elsif ($element_state->{phase} eq 'in colgroup') {
4030     if ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
4031     $element_state->{phase} = 'in colgroup';
4032     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
4033     $element_state->{phase} = 'after thead';
4034     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4035     $element_state->{phase} = 'in tbodys';
4036     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4037     $element_state->{phase} = 'in trs';
4038     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4039     $element_state->{phase} = 'in tbodys';
4040     $element_state->{has_tfoot} = 1;
4041     } else {
4042     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4043     }
4044     } elsif ($element_state->{phase} eq 'before caption') {
4045     if ($child_nsuri eq $HTML_NS and $child_ln eq 'caption') {
4046     $element_state->{phase} = 'in colgroup';
4047     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
4048     $element_state->{phase} = 'in colgroup';
4049     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
4050     $element_state->{phase} = 'after thead';
4051     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4052     $element_state->{phase} = 'in tbodys';
4053     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4054     $element_state->{phase} = 'in trs';
4055     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4056     $element_state->{phase} = 'in tbodys';
4057     $element_state->{has_tfoot} = 1;
4058     } else {
4059     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4060     }
4061     } elsif ($element_state->{phase} eq 'after tfoot') {
4062     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4063     } else {
4064     die "check_child_element: Bad |table| phase: $element_state->{phase}";
4065     }
4066     },
4067     check_child_text => sub {
4068     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4069     if ($has_significant) {
4070     $self->{onerror}->(node => $child_node, type => 'character not allowed');
4071 wakaba 1.1 }
4072 wakaba 1.40 },
4073     check_end => sub {
4074     my ($self, $item, $element_state) = @_;
4075 wakaba 1.1
4076     ## Table model errors
4077     require Whatpm::HTMLTable;
4078 wakaba 1.40 Whatpm::HTMLTable->form_table ($item->{node}, sub {
4079 wakaba 1.1 my %opt = @_;
4080 wakaba 1.84 $opt{type} = 'table:' . $opt{type};
4081     $self->{onerror}->(%opt);
4082 wakaba 1.1 });
4083 wakaba 1.40 push @{$self->{return}->{table}}, $item->{node};
4084 wakaba 1.1
4085 wakaba 1.40 $HTMLChecker{check_end}->(@_);
4086 wakaba 1.1 },
4087     };
4088    
4089     $Element->{$HTML_NS}->{caption} = {
4090 wakaba 1.40 %HTMLPhrasingContentChecker,
4091 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4092 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4093     align => $GetHTMLEnumeratedAttrChecker->({
4094     top => 1, bottom => 1, left => 1, right => 1,
4095     }),
4096     }, {
4097 wakaba 1.49 %HTMLAttrStatus,
4098 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4099 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
4100 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4101 wakaba 1.49 }),
4102 wakaba 1.1 };
4103    
4104 wakaba 1.69 my %cellalign = (
4105     ## HTML4 %cellhalign;
4106 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
4107     left => 1, center => 1, right => 1, justify => 1, char => 1,
4108     }),
4109     char => sub {
4110     my ($self, $attr) = @_;
4111 wakaba 1.69
4112 wakaba 1.70 ## NOTE: "character" or |%Character;| in HTML4.
4113    
4114     my $value = $attr->value;
4115     if (length $value != 1) {
4116     $self->{onerror}->(node => $attr, type => 'char:syntax error',
4117     level => $self->{fact_level}); ## TODO: type
4118     }
4119     },
4120     ## TODO: HTML4 |charoff|
4121 wakaba 1.69 ## HTML4 %cellvalign;
4122 wakaba 1.70 valign => $GetHTMLEnumeratedAttrChecker->({
4123     top => 1, middle => 1, bottom => 1, baseline => 1,
4124     }),
4125 wakaba 1.69 );
4126    
4127 wakaba 1.1 $Element->{$HTML_NS}->{colgroup} = {
4128 wakaba 1.40 %HTMLEmptyChecker,
4129 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4130 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4131 wakaba 1.69 %cellalign,
4132 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4133     ## NOTE: Defined only if "the |colgroup| element contains no |col| elements"
4134     ## TODO: "attribute not supported" if |col|.
4135     ## ISSUE: MUST NOT if any |col|?
4136     ## ISSUE: MUST NOT for |<colgroup span="1"><any><col/></any></colgroup>| (though non-conforming)?
4137 wakaba 1.49 }, {
4138     %HTMLAttrStatus,
4139 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4140 wakaba 1.49 align => FEATURE_M12N10_REC,
4141     char => FEATURE_M12N10_REC,
4142     charoff => FEATURE_M12N10_REC,
4143 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4144 wakaba 1.82 span => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4145 wakaba 1.49 valign => FEATURE_M12N10_REC,
4146     width => FEATURE_M12N10_REC,
4147 wakaba 1.1 }),
4148 wakaba 1.40 check_child_element => sub {
4149     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4150     $child_is_transparent, $element_state) = @_;
4151     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4152     $self->{onerror}->(node => $child_el,
4153     type => 'element not allowed:minus',
4154     level => $self->{must_level});
4155     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4156     #
4157     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'col') {
4158     #
4159     } else {
4160     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4161     }
4162     },
4163     check_child_text => sub {
4164     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4165     if ($has_significant) {
4166     $self->{onerror}->(node => $child_node, type => 'character not allowed');
4167 wakaba 1.1 }
4168     },
4169     };
4170    
4171     $Element->{$HTML_NS}->{col} = {
4172 wakaba 1.40 %HTMLEmptyChecker,
4173 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4174 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4175 wakaba 1.69 %cellalign,
4176 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4177 wakaba 1.49 }, {
4178     %HTMLAttrStatus,
4179 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4180 wakaba 1.49 align => FEATURE_M12N10_REC,
4181     char => FEATURE_M12N10_REC,
4182     charoff => FEATURE_M12N10_REC,
4183 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4184 wakaba 1.82 span => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4185 wakaba 1.49 valign => FEATURE_M12N10_REC,
4186     width => FEATURE_M12N10_REC,
4187 wakaba 1.1 }),
4188     };
4189    
4190     $Element->{$HTML_NS}->{tbody} = {
4191 wakaba 1.40 %HTMLChecker,
4192 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4193 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4194     %cellalign,
4195     }, {
4196 wakaba 1.49 %HTMLAttrStatus,
4197 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4198 wakaba 1.49 align => FEATURE_M12N10_REC,
4199     char => FEATURE_M12N10_REC,
4200     charoff => FEATURE_M12N10_REC,
4201 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4202 wakaba 1.49 valign => FEATURE_M12N10_REC,
4203     }),
4204 wakaba 1.40 check_child_element => sub {
4205     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4206     $child_is_transparent, $element_state) = @_;
4207     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4208     $self->{onerror}->(node => $child_el,
4209     type => 'element not allowed:minus',
4210     level => $self->{must_level});
4211     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4212     #
4213     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4214 wakaba 1.84 #
4215 wakaba 1.40 } else {
4216     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4217     }
4218     },
4219     check_child_text => sub {
4220     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4221     if ($has_significant) {
4222     $self->{onerror}->(node => $child_node, type => 'character not allowed');
4223 wakaba 1.1 }
4224 wakaba 1.40 },
4225 wakaba 1.1 };
4226    
4227     $Element->{$HTML_NS}->{thead} = {
4228 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
4229 wakaba 1.1 };
4230    
4231     $Element->{$HTML_NS}->{tfoot} = {
4232 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
4233 wakaba 1.1 };
4234    
4235     $Element->{$HTML_NS}->{tr} = {
4236 wakaba 1.40 %HTMLChecker,
4237 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4238 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4239     %cellalign,
4240     bgcolor => $HTMLColorAttrChecker,
4241     }, {
4242 wakaba 1.49 %HTMLAttrStatus,
4243 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4244 wakaba 1.49 align => FEATURE_M12N10_REC,
4245     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
4246     char => FEATURE_M12N10_REC,
4247     charoff => FEATURE_M12N10_REC,
4248 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4249 wakaba 1.49 valign => FEATURE_M12N10_REC,
4250     }),
4251 wakaba 1.40 check_child_element => sub {
4252     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4253     $child_is_transparent, $element_state) = @_;
4254     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4255     $self->{onerror}->(node => $child_el,
4256     type => 'element not allowed:minus',
4257     level => $self->{must_level});
4258     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4259     #
4260     } elsif ($child_nsuri eq $HTML_NS and
4261     ($child_ln eq 'td' or $child_ln eq 'th')) {
4262 wakaba 1.84 #
4263 wakaba 1.40 } else {
4264     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4265     }
4266     },
4267     check_child_text => sub {
4268     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4269     if ($has_significant) {
4270     $self->{onerror}->(node => $child_node, type => 'character not allowed');
4271 wakaba 1.1 }
4272     },
4273     };
4274    
4275     $Element->{$HTML_NS}->{td} = {
4276 wakaba 1.72 %HTMLFlowContentChecker,
4277 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4278 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4279 wakaba 1.69 %cellalign,
4280     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
4281     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
4282     bgcolor => $HTMLColorAttrChecker,
4283 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4284 wakaba 1.70 ## TODO: HTML5 |headers|
4285 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
4286 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4287 wakaba 1.69 scope => $GetHTMLEnumeratedAttrChecker
4288     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
4289 wakaba 1.49 }, {
4290     %HTMLAttrStatus,
4291 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4292     abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4293 wakaba 1.49 align => FEATURE_M12N10_REC,
4294 wakaba 1.82 axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4295 wakaba 1.49 bgcolor => FEATURE_M12N10_REC_DEPRECATED,
4296     char => FEATURE_M12N10_REC,
4297     charoff => FEATURE_M12N10_REC,
4298 wakaba 1.82 colspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4299     headers => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4300 wakaba 1.49 height => FEATURE_M12N10_REC_DEPRECATED,
4301 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4302 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
4303 wakaba 1.82 rowspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4304     scope => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4305 wakaba 1.49 valign => FEATURE_M12N10_REC,
4306     width => FEATURE_M12N10_REC_DEPRECATED,
4307 wakaba 1.1 }),
4308     };
4309    
4310     $Element->{$HTML_NS}->{th} = {
4311 wakaba 1.40 %HTMLPhrasingContentChecker,
4312 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4313 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4314 wakaba 1.69 %cellalign,
4315     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
4316     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
4317     bgcolor => $HTMLColorAttrChecker,
4318 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4319 wakaba 1.70 ## TODO: HTML5 |headers|
4320 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
4321 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4322     scope => $GetHTMLEnumeratedAttrChecker
4323     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
4324 wakaba 1.49 }, {
4325     %HTMLAttrStatus,
4326 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4327     abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4328 wakaba 1.49 align => FEATURE_M12N10_REC,
4329 wakaba 1.82 axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4330 wakaba 1.49 bgcolor => FEATURE_M12N10_REC_DEPRECATED,
4331     char => FEATURE_M12N10_REC,
4332     charoff => FEATURE_M12N10_REC,
4333 wakaba 1.82 colspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4334     headers => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4335 wakaba 1.49 height => FEATURE_M12N10_REC_DEPRECATED,
4336 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4337 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
4338 wakaba 1.82 rowspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4339     scope => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4340 wakaba 1.49 valign => FEATURE_M12N10_REC,
4341     width => FEATURE_M12N10_REC_DEPRECATED,
4342 wakaba 1.1 }),
4343     };
4344    
4345 wakaba 1.52 my $AttrCheckerNotImplemented = sub {
4346     my ($self, $attr) = @_;
4347     $self->{onerror}->(node => $attr, level => 'unsupported',
4348     type => 'attribute');
4349     };
4350    
4351     $Element->{$HTML_NS}->{form} = {
4352 wakaba 1.72 %HTMLFlowContentChecker, ## NOTE: Flow* [WF2]
4353 wakaba 1.56 ## TODO: form in form is allowed in XML [WF2]
4354 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
4355     check_attrs => $GetHTMLAttrsChecker->({
4356 wakaba 1.56 accept => $AttrCheckerNotImplemented, ## TODO: ContentTypes [WF2]
4357 wakaba 1.52 'accept-charset' => $AttrCheckerNotImplemented, ## TODO: Charsets
4358     action => $HTMLURIAttrChecker, ## TODO: "User agent behavior for a value other than HTTP URI is undefined" [HTML4]
4359 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
4360     enctype => $HTMLIMTAttrChecker, ## TODO: "multipart/form-data" should be used when type=file is used [HTML4] ## TODO: MUST NOT parameter [WF2]
4361     method => $GetHTMLEnumeratedAttrChecker->({
4362     get => 1, post => 1, put => 1, delete => 1,
4363     }),
4364 wakaba 1.52 ## NOTE: "get" SHOULD be used for idempotent submittion,
4365     ## "post" SHOULD be used otherwise [HTML4]. This cannot be tested.
4366     name => sub { }, # CDATA in HTML4 ## TODO: must be same as |id| (informative!) [XHTML10]
4367 wakaba 1.56 onreceived => $HTMLEventHandlerAttrChecker,
4368     replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
4369 wakaba 1.52 target => $HTMLTargetAttrChecker,
4370     ## TODO: Warn for combination whose behavior is not defined.
4371     }, {
4372     %HTMLAttrStatus,
4373     %HTMLM12NCommonAttrStatus,
4374 wakaba 1.56 accept => FEATURE_WF2 | FEATURE_M12N10_REC,
4375 wakaba 1.52 'accept-charset' => FEATURE_M12N10_REC,
4376 wakaba 1.56 action => FEATURE_WF2 | FEATURE_M12N10_REC,
4377     data => FEATURE_WF2,
4378     enctype => FEATURE_WF2 | FEATURE_M12N10_REC,
4379 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4380 wakaba 1.56 method => FEATURE_WF2 | FEATURE_M12N10_REC,
4381 wakaba 1.52 name => FEATURE_M12N10_REC_DEPRECATED,
4382 wakaba 1.56 onreceived => FEATURE_WF2,
4383 wakaba 1.52 onreset => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4384     onsubmit => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4385 wakaba 1.56 replace => FEATURE_WF2,
4386 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
4387     sdasuff => FEATURE_HTML20_RFC,
4388 wakaba 1.52 target => FEATURE_M12N10_REC,
4389     }),
4390     ## TODO: Tests
4391     ## TODO: Tests for <nest/> in <form>
4392 wakaba 1.66 check_start => sub {
4393     my ($self, $item, $element_state) = @_;
4394    
4395     $element_state->{uri_info}->{action}->{type}->{action} = 1;
4396     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
4397 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4398     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4399 wakaba 1.66 },
4400 wakaba 1.52 };
4401    
4402     $Element->{$HTML_NS}->{fieldset} = {
4403 wakaba 1.72 %HTMLFlowContentChecker, ## NOTE: legend, %Flow; ## TODO: legend
4404 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
4405 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
4406     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4407     ## TODO: form [WF2]
4408     }, {
4409 wakaba 1.52 %HTMLAttrStatus,
4410     %HTMLM12NCommonAttrStatus,
4411 wakaba 1.56 disabled => FEATURE_WF2,
4412     form => FEATURE_WF2,
4413 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4414     }),
4415     ## TODO: Tests
4416     ## TODO: Tests for <nest/> in <fieldset>
4417     };
4418    
4419     $Element->{$HTML_NS}->{input} = {
4420 wakaba 1.56 %HTMLEmptyChecker, ## MUST [WF2]
4421 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
4422     check_attrs => $GetHTMLAttrsChecker->({
4423 wakaba 1.56 accept => $AttrCheckerNotImplemented, ## TODO: ContentTypes [WF2]
4424 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4425 wakaba 1.56 action => $HTMLURIAttrChecker,
4426 wakaba 1.52 align => $GetHTMLEnumeratedAttrChecker->({
4427     top => 1, middle => 1, bottom => 1, left => 1, right => 1,
4428     }),
4429     alt => sub {}, ## NOTE: Text [M12N] ## TODO: |alt| should be provided for |type=image| [HTML4]
4430     ## NOTE: HTML4 has a "should" for accessibility, which cannot be tested
4431     ## here.
4432 wakaba 1.56 autocomplete => $GetHTMLEnumeratedAttrChecker->({on => 1, off => 1}),
4433     autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
4434 wakaba 1.52 checked => $GetHTMLBooleanAttrChecker->('checked'),
4435     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4436 wakaba 1.56 enctype => $HTMLIMTAttrChecker,
4437     ## TODO: form [WF2]
4438     ## TODO: inputmode [WF2]
4439 wakaba 1.52 ismap => $GetHTMLBooleanAttrChecker->('ismap'),
4440 wakaba 1.56 ## TODO: list [WF2]
4441     ## TODO: max [WF2]
4442 wakaba 1.52 maxlength => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4443 wakaba 1.56 method => $GetHTMLEnumeratedAttrChecker->({
4444     get => 1, post => 1, put => 1, delete => 1,
4445     }),
4446     ## TODO: min [WF2]
4447 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
4448     readonly => $GetHTMLBooleanAttrChecker->('readonly'),
4449 wakaba 1.56 replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
4450     required => $GetHTMLBooleanAttrChecker->('required'),
4451 wakaba 1.52 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4452     src => $HTMLURIAttrChecker,
4453 wakaba 1.56 ## TODO: step [WF2]
4454     target => $HTMLTargetAttrChecker,
4455 wakaba 1.80 ## NOTE: According to Web Forms 2.0, |input| attribute has |template|
4456     ## attribute to support the |add| button type (as part of repetition
4457     ## template feature). It conflicts with the |template| global attribute
4458     ## introduced as part of the data template feature.
4459     ## NOTE: |template| attribute as defined in Web Forms 2.0 has no
4460     ## author requirement.
4461 wakaba 1.52 type => $GetHTMLEnumeratedAttrChecker->({
4462     text => 1, password => 1, checkbox => 1, radio => 1, submit => 1,
4463     reset => 1, file => 1, hidden => 1, image => 1, button => 1,
4464 wakaba 1.56 ## [WF2]
4465     datatime => 1, 'datetime-local' => 1, date => 1, month => 1, week => 1,
4466     time => 1, number => 1, range => 1, email => 1, url => 1,
4467     add => 1, remove => 1, 'move-up' => 1, 'move-down' => 1,
4468 wakaba 1.52 }),
4469     usemap => $HTMLUsemapAttrChecker,
4470 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]
4471     ## TODO: "authors should ensure that in each set of radio buttons that one is initially "on"." [HTML4] [WF2]
4472 wakaba 1.52 }, {
4473     %HTMLAttrStatus,
4474     %HTMLM12NCommonAttrStatus,
4475 wakaba 1.56 accept => FEATURE_WF2 | FEATURE_M12N10_REC,
4476 wakaba 1.61 'accept-charset' => FEATURE_HTML2X_RFC,
4477 wakaba 1.52 accesskey => FEATURE_M12N10_REC,
4478 wakaba 1.56 action => FEATURE_WF2,
4479 wakaba 1.52 align => FEATURE_M12N10_REC_DEPRECATED,
4480     alt => FEATURE_M12N10_REC,
4481 wakaba 1.56 autocomplete => FEATURE_WF2,
4482     autofocus => FEATURE_WF2,
4483 wakaba 1.52 checked => FEATURE_M12N10_REC,
4484     datafld => FEATURE_HTML4_REC_RESERVED,
4485     dataformatas => FEATURE_HTML4_REC_RESERVED,
4486     datasrc => FEATURE_HTML4_REC_RESERVED,
4487 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
4488 wakaba 1.65 enctype => FEATURE_WF2,
4489 wakaba 1.56 form => FEATURE_WF2,
4490     inputmode => FEATURE_WF2 | FEATURE_XHTMLBASIC11_CR,
4491 wakaba 1.52 ismap => FEATURE_M12N10_REC,
4492     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4493 wakaba 1.56 list => FEATURE_WF2,
4494     max => FEATURE_WF2,
4495     maxlength => FEATURE_WF2 | FEATURE_M12N10_REC,
4496     method => FEATURE_WF2,
4497     min => FEATURE_WF2,
4498 wakaba 1.52 name => FEATURE_M12N10_REC,
4499     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4500     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4501     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4502     onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4503 wakaba 1.56 readonly => FEATURE_WF2 | FEATURE_M12N10_REC,
4504 wakaba 1.65 replace => FEATURE_WF2,
4505 wakaba 1.56 required => FEATURE_WF2,
4506 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
4507 wakaba 1.56 size => FEATURE_WF2_DEPRECATED | FEATURE_M12N10_REC,
4508 wakaba 1.52 src => FEATURE_M12N10_REC,
4509 wakaba 1.56 step => FEATURE_WF2,
4510 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4511 wakaba 1.65 target => FEATURE_WF2,
4512 wakaba 1.56 template => FEATURE_WF2,
4513 wakaba 1.52 type => FEATURE_M12N10_REC,
4514     usemap => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC,
4515     value => FEATURE_M12N10_REC,
4516     }),
4517     ## TODO: Tests
4518     ## TODO: Tests for <nest/> in <input>
4519 wakaba 1.66 check_start => sub {
4520     my ($self, $item, $element_state) = @_;
4521    
4522     $element_state->{uri_info}->{action}->{type}->{action} = 1;
4523     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4524     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4525 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4526     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4527 wakaba 1.66 },
4528 wakaba 1.52 };
4529    
4530 wakaba 1.56 ## TODO: Form |name| attributes: MUST NOT conflict with RFC 3106 [WF2]
4531    
4532 wakaba 1.80 ## NOTE: "authors who are nesting repetition blocks should position such
4533     ## [repetition-block-related] buttons carefully to make clear which block a
4534     ## button applies to.": I have no idea how this can be tested.
4535    
4536 wakaba 1.52 $Element->{$HTML_NS}->{button} = {
4537 wakaba 1.72 %HTMLFlowContentChecker, ## NOTE: %Flow; - something [XHTML10]
4538 wakaba 1.52 ## TODO: -A|%formctrl;|form|fieldset [HTML4]
4539     ## TODO: image map (img) in |button| is "illegal" [HTML4].
4540     status => FEATURE_WF2 | FEATURE_M12N10_REC,
4541     check_attrs => $GetHTMLAttrsChecker->({
4542 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4543 wakaba 1.56 action => $HTMLURIAttrChecker,
4544     autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
4545 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4546 wakaba 1.56 ## TODO: form [WF2]
4547     method => $GetHTMLEnumeratedAttrChecker->({
4548     get => 1, post => 1, put => 1, delete => 1,
4549     }),
4550 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
4551 wakaba 1.56 oninvalid => $HTMLEventHandlerAttrChecker,
4552     replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
4553     target => $HTMLTargetAttrChecker,
4554 wakaba 1.80 ## NOTE: According to Web Forms 2.0, |button| attribute has |template|
4555     ## attribute to support the |add| button type (as part of repetition
4556     ## template feature). It conflicts with the |template| global attribute
4557     ## introduced as part of the data template feature.
4558     ## NOTE: |template| attribute as defined in Web Forms 2.0 has no
4559     ## author requirement.
4560 wakaba 1.52 type => $GetHTMLEnumeratedAttrChecker->({
4561     button => 1, submit => 1, reset => 1,
4562 wakaba 1.80 ## [WF2]
4563     add => 1, remove => 1, 'move-up' => 1, 'move-down' => 1,
4564 wakaba 1.52 }),
4565     value => sub {}, ## NOTE: CDATA [M12N]
4566     }, {
4567     %HTMLAttrStatus,
4568     %HTMLM12NCommonAttrStatus,
4569     accesskey => FEATURE_M12N10_REC,
4570 wakaba 1.56 action => FEATURE_WF2,
4571     autofocus => FEATURE_WF2,
4572 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
4573     dataformatas => FEATURE_HTML4_REC_RESERVED,
4574     datasrc => FEATURE_HTML4_REC_RESERVED,
4575 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
4576     enctype => FEATURE_WF2,
4577     form => FEATURE_WF2,
4578 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4579 wakaba 1.56 method => FEATURE_WF2,
4580 wakaba 1.52 name => FEATURE_M12N10_REC,
4581     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4582     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4583 wakaba 1.56 oninvalid => FEATURE_WF2,
4584     replace => FEATURE_WF2,
4585 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4586 wakaba 1.56 target => FEATURE_WF2,
4587     template => FEATURE_WF2,
4588 wakaba 1.52 type => FEATURE_M12N10_REC,
4589     value => FEATURE_M12N10_REC,
4590     }),
4591     ## TODO: Tests
4592     ## TODO: Tests for <nest/> in <button>
4593 wakaba 1.66 check_start => sub {
4594     my ($self, $item, $element_state) = @_;
4595    
4596     $element_state->{uri_info}->{action}->{type}->{action} = 1;
4597     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4598 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4599     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4600 wakaba 1.66 },
4601 wakaba 1.52 };
4602    
4603     $Element->{$HTML_NS}->{label} = {
4604     %HTMLPhrasingContentChecker, ## NOTE: %Inline - label [XHTML10] ## TODO: -label
4605 wakaba 1.56 ## TODO: At most one form control [WF2]
4606 wakaba 1.82 status => FEATURE_WF2 | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4607 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
4608 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4609 wakaba 1.52 for => $AttrCheckerNotImplemented, ## TODO: IDREF ## TODO: Must be |id| of control [HTML4] ## TODO: Or, "may only contain one control element"
4610     }, {
4611     %HTMLAttrStatus,
4612 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4613 wakaba 1.56 accesskey => FEATURE_WF2 | FEATURE_M12N10_REC,
4614 wakaba 1.52 for => FEATURE_M12N10_REC,
4615     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4616     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4617     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4618     }),
4619     ## TODO: Tests
4620     ## TODO: Tests for <nest/> in <label>
4621     };
4622    
4623     $Element->{$HTML_NS}->{select} = {
4624 wakaba 1.72 %HTMLFlowContentChecker, ## TODO: (optgroup|option)* [HTML4] + [WF2] ## TODO: SHOULD avoid empty and visible [WF2]
4625 wakaba 1.52 ## TODO: author should SELECTED at least one OPTION in non-MULTIPLE case [HTML4].
4626     ## TODO: more than one OPTION with SELECTED in non-MULTIPLE case is "error" [HTML4]
4627     status => FEATURE_WF2 | FEATURE_M12N10_REC,
4628 wakaba 1.56 is_root => 1, ## TODO: SHOULD NOT in application/xhtml+xml [WF2]
4629 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
4630 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4631 wakaba 1.56 autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
4632 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4633 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
4634     ## TODO: form [WF2]
4635 wakaba 1.52 multiple => $GetHTMLBooleanAttrChecker->('multiple'),
4636     name => sub {}, ## NOTE: CDATA [M12N]
4637 wakaba 1.56 oninvalid => $HTMLEventHandlerAttrChecker,
4638     ## TODO: pattern [WF2] ## TODO: |title| semantics
4639 wakaba 1.52 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4640     }, {
4641     %HTMLAttrStatus,
4642     %HTMLM12NCommonAttrStatus,
4643 wakaba 1.56 accesskey => FEATURE_WF2,
4644     autofocus => FEATURE_WF2,
4645     data => FEATURE_WF2,
4646 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
4647     dataformatas => FEATURE_HTML4_REC_RESERVED,
4648     datasrc => FEATURE_HTML4_REC_RESERVED,
4649 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
4650     form => FEATURE_WF2,
4651 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4652     multiple => FEATURE_M12N10_REC,
4653     name => FEATURE_M12N10_REC,
4654     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4655     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4656     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4657 wakaba 1.56 oninvalid => FEATURE_WF2,
4658     pattern => FEATURE_WF2,
4659 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
4660     sdapref => FEATURE_HTML20_RFC,
4661 wakaba 1.52 size => FEATURE_M12N10_REC,
4662     tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4663     }),
4664     ## TODO: Tests
4665     ## TODO: Tests for <nest/> in <select>
4666 wakaba 1.66 check_start => sub {
4667     my ($self, $item, $element_state) = @_;
4668    
4669     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
4670     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4671 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4672     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4673 wakaba 1.66 },
4674 wakaba 1.52 };
4675 wakaba 1.1
4676 wakaba 1.52 $Element->{$HTML_NS}->{datalist} = {
4677 wakaba 1.72 %HTMLFlowContentChecker, ## TODO: (transparent | option)*
4678 wakaba 1.56 ## TODO: |option| child MUST be empty [WF2]
4679 wakaba 1.52 status => FEATURE_WF2,
4680 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
4681     data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
4682     }, {
4683 wakaba 1.52 %HTMLAttrStatus,
4684 wakaba 1.56 data => FEATURE_WF2,
4685 wakaba 1.52 }),
4686     ## TODO: Tests
4687     ## TODO: Tests for <nest/> in <datalist>
4688 wakaba 1.66 check_start => sub {
4689     my ($self, $item, $element_state) = @_;
4690    
4691     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
4692 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4693     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4694 wakaba 1.66 },
4695 wakaba 1.52 };
4696 wakaba 1.49
4697 wakaba 1.52 $Element->{$HTML_NS}->{optgroup} = {
4698 wakaba 1.72 %HTMLFlowContentChecker, ## TODO: (option|optgroup)* [HTML4] + [WF2] SHOULD avoid empty and visible [WF2]
4699 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
4700     check_attrs => $GetHTMLAttrsChecker->({
4701     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4702     label => sub {}, ## NOTE: Text [M12N] ## TODO: required
4703     }, {
4704     %HTMLAttrStatus,
4705     %HTMLM12NCommonAttrStatus,
4706 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
4707 wakaba 1.52 label => FEATURE_M12N10_REC,
4708     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4709     }),
4710     ## TODO: Tests
4711     ## TODO: Tests for <nest/> in <optgroup>
4712     };
4713    
4714     $Element->{$HTML_NS}->{option} = {
4715     %HTMLTextChecker,
4716     status => FEATURE_WF2 | FEATURE_M12N10_REC,
4717     check_attrs => $GetHTMLAttrsChecker->({
4718     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4719     label => sub {}, ## NOTE: Text [M12N]
4720     selected => $GetHTMLBooleanAttrChecker->('selected'),
4721     value => sub {}, ## NOTE: CDATA [M12N]
4722     }, {
4723     %HTMLAttrStatus,
4724     %HTMLM12NCommonAttrStatus,
4725 wakaba 1.56 disabled => FEATURE_WF2, FEATURE_M12N10_REC,
4726 wakaba 1.52 label => FEATURE_M12N10_REC,
4727     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4728 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
4729     sdapref => FEATURE_HTML20_RFC,
4730 wakaba 1.52 selected => FEATURE_M12N10_REC,
4731     value => FEATURE_M12N10_REC,
4732     }),
4733     ## TODO: Tests
4734     ## TODO: Tests for <nest/> in <option>
4735     };
4736 wakaba 1.49
4737 wakaba 1.52 $Element->{$HTML_NS}->{textarea} = {
4738     %HTMLTextChecker,
4739     status => FEATURE_WF2 | FEATURE_M12N10_REC,
4740     check_attrs => $GetHTMLAttrsChecker->({
4741 wakaba 1.56 accept => $HTMLIMTAttrChecker, ## TODO: MUST be a text-based type
4742 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4743 wakaba 1.56 autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
4744     cols => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## TODO: SHOULD if wrap=hard [WF2]
4745 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4746 wakaba 1.56 ## TODO: form [WF2]
4747     ## TODO: inputmode [WF2]
4748     maxlength => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4749 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
4750 wakaba 1.56 ## TODO: pattern [WF2] ## TODO: |title| special semantics
4751 wakaba 1.52 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
4752 wakaba 1.56 required => $GetHTMLBooleanAttrChecker->('required'),
4753     rows => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4754     oninvalid => $HTMLEventHandlerAttrChecker,
4755     wrap => $GetHTMLEnumeratedAttrChecker->({soft => 1, hard => 1}),
4756 wakaba 1.52 }, {
4757     %HTMLAttrStatus,
4758     %HTMLM12NCommonAttrStatus,
4759 wakaba 1.56 accept => FEATURE_WF2,
4760 wakaba 1.61 'accept-charset' => FEATURE_HTML2X_RFC,
4761 wakaba 1.52 accesskey => FEATURE_M12N10_REC,
4762 wakaba 1.56 autofocus => FEATURE_WF2,
4763 wakaba 1.52 cols => FEATURE_M12N10_REC,
4764     datafld => FEATURE_HTML4_REC_RESERVED,
4765 wakaba 1.49 dataformatas => FEATURE_HTML4_REC_RESERVED,
4766     datasrc => FEATURE_HTML4_REC_RESERVED,
4767 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
4768     form => FEATURE_WF2,
4769     inputmode => FEATURE_WF2 | FEATURE_XHTMLBASIC11_CR,
4770 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4771 wakaba 1.56 maxlength => FEATURE_WF2,
4772 wakaba 1.52 name => FEATURE_M12N10_REC,
4773     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4774     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4775     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4776 wakaba 1.56 oninvalid => FEATURE_WF2,
4777 wakaba 1.52 onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4778 wakaba 1.56 pattern => FEATURE_WF2,
4779     readonly => FEATURE_WF2 | FEATURE_M12N10_REC,
4780     required => FEATURE_WF2,
4781 wakaba 1.61 rows => FEATURE_M12N10_REC,
4782     sdaform => FEATURE_HTML20_RFC,
4783     sdapref => FEATURE_HTML20_RFC,
4784 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4785 wakaba 1.56 wrap => FEATURE_WF2,
4786 wakaba 1.52 }),
4787     ## TODO: Tests
4788     ## TODO: Tests for <nest/> in <textarea>
4789 wakaba 1.66 check_start => sub {
4790     my ($self, $item, $element_state) = @_;
4791    
4792     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
4793 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4794     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4795 wakaba 1.66 },
4796 wakaba 1.52 };
4797 wakaba 1.49
4798 wakaba 1.52 $Element->{$HTML_NS}->{output} = {
4799 wakaba 1.56 %HTMLPhrasingContentChecker, ## Inline [WF2]
4800 wakaba 1.52 status => FEATURE_WF2,
4801 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
4802     ## TODO: for [WF2]
4803     ## TODO: form [WF2]
4804     ## TODO: name [WF2]
4805     ## onformchange[WF2]
4806     ## onforminput[WF2]
4807     }, {
4808 wakaba 1.52 %HTMLAttrStatus,
4809 wakaba 1.56 for => FEATURE_WF2,
4810     form => FEATURE_WF2,
4811     name => FEATURE_WF2,
4812     onchange => FEATURE_HTML5_DEFAULT | FEATURE_WF2,
4813     onformchange => FEATURE_WF2,
4814     onforminput => FEATURE_WF2,
4815 wakaba 1.52 }),
4816     ## TODO: Tests
4817     ## TODO: Tests for <nest/> in <output>
4818 wakaba 1.56 ## NOTE: "The output element should be used when ..." [WF2]
4819 wakaba 1.52 };
4820    
4821     ## TODO: repetition template
4822    
4823     $Element->{$HTML_NS}->{isindex} = {
4824     %HTMLEmptyChecker,
4825 wakaba 1.54 status => FEATURE_M12N10_REC_DEPRECATED |
4826     Whatpm::ContentChecker::FEATURE_DEPRECATED_SHOULD, ## [HTML4]
4827 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
4828     prompt => sub {}, ## NOTE: Text [M12N]
4829     }, {
4830     %HTMLAttrStatus,
4831     class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4832     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4833     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4834     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4835     prompt => FEATURE_M12N10_REC_DEPRECATED,
4836 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
4837 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4838 wakaba 1.52 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4839     }),
4840     ## TODO: Tests
4841     ## TODO: Tests for <nest/> in <isindex>
4842 wakaba 1.66 check_start => sub {
4843     my ($self, $item, $element_state) = @_;
4844    
4845     $element_state->{uri_info}->{action}->{type}->{action} = 1;
4846 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4847     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4848 wakaba 1.66 },
4849 wakaba 1.52 };
4850 wakaba 1.49
4851 wakaba 1.1 $Element->{$HTML_NS}->{script} = {
4852 wakaba 1.40 %HTMLChecker,
4853 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4854 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4855 wakaba 1.70 ## TODO: HTML4 |charset|
4856     ## TODO: HTML4 |language|
4857 wakaba 1.1 src => $HTMLURIAttrChecker,
4858     defer => $GetHTMLBooleanAttrChecker->('defer'),
4859     async => $GetHTMLBooleanAttrChecker->('async'),
4860     type => $HTMLIMTAttrChecker,
4861 wakaba 1.49 }, {
4862     %HTMLAttrStatus,
4863     %HTMLM12NCommonAttrStatus,
4864 wakaba 1.50 async => FEATURE_HTML5_DEFAULT,
4865 wakaba 1.49 charset => FEATURE_M12N10_REC,
4866 wakaba 1.50 defer => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4867 wakaba 1.49 event => FEATURE_HTML4_REC_RESERVED,
4868     for => FEATURE_HTML4_REC_RESERVED,
4869 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4870 wakaba 1.49 language => FEATURE_M12N10_REC_DEPRECATED,
4871 wakaba 1.50 src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4872     type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4873 wakaba 1.9 }),
4874 wakaba 1.40 check_start => sub {
4875     my ($self, $item, $element_state) = @_;
4876 wakaba 1.1
4877 wakaba 1.40 if ($item->{node}->has_attribute_ns (undef, 'src')) {
4878     $element_state->{must_be_empty} = 1;
4879 wakaba 1.1 } else {
4880     ## NOTE: No content model conformance in HTML5 spec.
4881 wakaba 1.40 my $type = $item->{node}->get_attribute_ns (undef, 'type');
4882     my $language = $item->{node}->get_attribute_ns (undef, 'language');
4883 wakaba 1.1 if ((defined $type and $type eq '') or
4884     (defined $language and $language eq '')) {
4885     $type = 'text/javascript';
4886     } elsif (defined $type) {
4887     #
4888     } elsif (defined $language) {
4889     $type = 'text/' . $language;
4890     } else {
4891     $type = 'text/javascript';
4892     }
4893 wakaba 1.40 $element_state->{script_type} = $type; ## TODO: $type normalization
4894     }
4895 wakaba 1.66
4896     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
4897 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4898     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4899 wakaba 1.40 },
4900     check_child_element => sub {
4901     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4902     $child_is_transparent, $element_state) = @_;
4903     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4904     $self->{onerror}->(node => $child_el,
4905     type => 'element not allowed:minus',
4906     level => $self->{must_level});
4907     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4908     #
4909     } else {
4910     if ($element_state->{must_be_empty}) {
4911     $self->{onerror}->(node => $child_el,
4912     type => 'element not allowed');
4913     }
4914     }
4915     },
4916     check_child_text => sub {
4917     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4918     if ($has_significant and
4919     $element_state->{must_be_empty}) {
4920     $self->{onerror}->(node => $child_node,
4921     type => 'character not allowed');
4922     }
4923     },
4924     check_end => sub {
4925     my ($self, $item, $element_state) = @_;
4926     unless ($element_state->{must_be_empty}) {
4927     $self->{onerror}->(node => $item->{node}, level => 'unsupported',
4928     type => 'script:'.$element_state->{script_type});
4929     ## TODO: text/javascript support
4930    
4931     $HTMLChecker{check_end}->(@_);
4932 wakaba 1.1 }
4933     },
4934     };
4935 wakaba 1.25 ## ISSUE: Significant check and text child node
4936 wakaba 1.1
4937     ## NOTE: When script is disabled.
4938     $Element->{$HTML_NS}->{noscript} = {
4939 wakaba 1.40 %HTMLTransparentChecker,
4940 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4941     check_attrs => $GetHTMLAttrsChecker->({}, {
4942     %HTMLAttrStatus,
4943     %HTMLM12NCommonAttrStatus,
4944 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4945 wakaba 1.49 }),
4946 wakaba 1.40 check_start => sub {
4947     my ($self, $item, $element_state) = @_;
4948 wakaba 1.3
4949 wakaba 1.40 unless ($item->{node}->owner_document->manakai_is_html) {
4950     $self->{onerror}->(node => $item->{node}, type => 'in XML:noscript');
4951 wakaba 1.3 }
4952    
4953 wakaba 1.40 unless ($self->{flag}->{in_head}) {
4954     $self->_add_minus_elements ($element_state,
4955     {$HTML_NS => {noscript => 1}});
4956     }
4957 wakaba 1.79
4958     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4959     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4960 wakaba 1.3 },
4961 wakaba 1.40 check_child_element => sub {
4962     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4963     $child_is_transparent, $element_state) = @_;
4964     if ($self->{flag}->{in_head}) {
4965     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4966     $self->{onerror}->(node => $child_el,
4967     type => 'element not allowed:minus',
4968     level => $self->{must_level});
4969     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4970     #
4971     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'link') {
4972     #
4973     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') {
4974     if ($child_el->has_attribute_ns (undef, 'scoped')) {
4975     $self->{onerror}->(node => $child_el,
4976     type => 'element not allowed:head noscript',
4977     level => $self->{must_level});
4978     }
4979     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'meta') {
4980 wakaba 1.47 my $http_equiv_attr
4981     = $child_el->get_attribute_node_ns (undef, 'http-equiv');
4982     if ($http_equiv_attr) {
4983     ## TODO: case
4984     if (lc $http_equiv_attr->value eq 'content-type') {
4985 wakaba 1.40 $self->{onerror}->(node => $child_el,
4986 wakaba 1.34 type => 'element not allowed:head noscript',
4987     level => $self->{must_level});
4988 wakaba 1.47 } else {
4989     #
4990 wakaba 1.3 }
4991 wakaba 1.47 } else {
4992     $self->{onerror}->(node => $child_el,
4993     type => 'element not allowed:head noscript',
4994     level => $self->{must_level});
4995 wakaba 1.3 }
4996 wakaba 1.40 } else {
4997     $self->{onerror}->(node => $child_el,
4998     type => 'element not allowed:head noscript',
4999     level => $self->{must_level});
5000     }
5001     } else {
5002     $HTMLTransparentChecker{check_child_element}->(@_);
5003     }
5004     },
5005     check_child_text => sub {
5006     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5007     if ($self->{flag}->{in_head}) {
5008     if ($has_significant) {
5009     $self->{onerror}->(node => $child_node,
5010     type => 'character not allowed');
5011 wakaba 1.3 }
5012     } else {
5013 wakaba 1.40 $HTMLTransparentChecker{check_child_text}->(@_);
5014     }
5015     },
5016     check_end => sub {
5017     my ($self, $item, $element_state) = @_;
5018     $self->_remove_minus_elements ($element_state);
5019     if ($self->{flag}->{in_head}) {
5020     $HTMLChecker{check_end}->(@_);
5021     } else {
5022     $HTMLPhrasingContentChecker{check_end}->(@_);
5023 wakaba 1.3 }
5024 wakaba 1.1 },
5025     };
5026 wakaba 1.3 ## ISSUE: Scripting is disabled: <head><noscript><html a></noscript></head>
5027 wakaba 1.1
5028     $Element->{$HTML_NS}->{'event-source'} = {
5029 wakaba 1.40 %HTMLEmptyChecker,
5030 wakaba 1.48 status => FEATURE_HTML5_LC,
5031 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5032 wakaba 1.1 src => $HTMLURIAttrChecker,
5033 wakaba 1.50 }, {
5034     %HTMLAttrStatus,
5035     src => FEATURE_HTML5_LC,
5036 wakaba 1.1 }),
5037 wakaba 1.66 check_start => sub {
5038     my ($self, $item, $element_state) = @_;
5039    
5040     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
5041 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5042     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5043 wakaba 1.66 },
5044 wakaba 1.1 };
5045    
5046     $Element->{$HTML_NS}->{details} = {
5047 wakaba 1.72 %HTMLFlowContentChecker,
5048 wakaba 1.48 status => FEATURE_HTML5_WD,
5049 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5050 wakaba 1.1 open => $GetHTMLBooleanAttrChecker->('open'),
5051 wakaba 1.50 }, {
5052     %HTMLAttrStatus,
5053 wakaba 1.59 open => FEATURE_HTML5_WD,
5054 wakaba 1.1 }),
5055 wakaba 1.72 ## NOTE: legend, Flow
5056 wakaba 1.43 check_child_element => sub {
5057     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5058     $child_is_transparent, $element_state) = @_;
5059     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5060     $self->{onerror}->(node => $child_el,
5061     type => 'element not allowed:minus',
5062     level => $self->{must_level});
5063     $element_state->{has_non_legend} = 1;
5064     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5065     #
5066     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') {
5067     if ($element_state->{has_non_legend}) {
5068     $self->{onerror}->(node => $child_el,
5069     type => 'element not allowed:details legend',
5070     level => $self->{must_level});
5071     }
5072     $element_state->{has_legend} = 1;
5073     $element_state->{has_non_legend} = 1;
5074     } else {
5075 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
5076 wakaba 1.43 $element_state->{has_non_legend} = 1 unless $child_is_transparent;
5077     ## ISSUE: |<details><object><legend>xx</legend></object>..</details>|
5078     ## is conforming?
5079     }
5080     },
5081     check_child_text => sub {
5082     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5083     if ($has_significant) {
5084     $element_state->{has_non_legend} = 1;
5085     }
5086     },
5087     check_end => sub {
5088     my ($self, $item, $element_state) = @_;
5089 wakaba 1.1
5090 wakaba 1.43 unless ($element_state->{has_legend}) {
5091     $self->{onerror}->(node => $item->{node},
5092     type => 'element missing:legend',
5093     level => $self->{must_level});
5094     }
5095    
5096 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
5097 wakaba 1.43 ## ISSUE: |<details><legend>aa</legend></details>| error?
5098 wakaba 1.1 },
5099     };
5100    
5101     $Element->{$HTML_NS}->{datagrid} = {
5102 wakaba 1.72 %HTMLFlowContentChecker,
5103 wakaba 1.48 status => FEATURE_HTML5_WD,
5104 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5105 wakaba 1.1 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5106     multiple => $GetHTMLBooleanAttrChecker->('multiple'),
5107 wakaba 1.50 }, {
5108     %HTMLAttrStatus,
5109     disabled => FEATURE_HTML5_WD,
5110     multiple => FEATURE_HTML5_WD,
5111 wakaba 1.1 }),
5112 wakaba 1.40 check_start => sub {
5113     my ($self, $item, $element_state) = @_;
5114 wakaba 1.1
5115 wakaba 1.40 $self->_add_minus_elements ($element_state,
5116     {$HTML_NS => {a => 1, datagrid => 1}});
5117     $element_state->{phase} = 'any';
5118 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5119     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5120 wakaba 1.40 },
5121 wakaba 1.72 ## Flow -(text* table Flow*) | table | select | datalist | Empty
5122 wakaba 1.40 check_child_element => sub {
5123     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5124     $child_is_transparent, $element_state) = @_;
5125     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5126     $self->{onerror}->(node => $child_el,
5127     type => 'element not allowed:minus',
5128     level => $self->{must_level});
5129     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5130     #
5131 wakaba 1.72 } elsif ($element_state->{phase} eq 'flow') {
5132     if ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) {
5133 wakaba 1.44 if (not $element_state->{has_element} and
5134 wakaba 1.40 $child_nsuri eq $HTML_NS and
5135     $child_ln eq 'table') {
5136     $self->{onerror}->(node => $child_el,
5137     type => 'element not allowed');
5138     } else {
5139 wakaba 1.8 #
5140 wakaba 1.1 }
5141 wakaba 1.40 } else {
5142     $self->{onerror}->(node => $child_el,
5143     type => 'element not allowed');
5144     }
5145 wakaba 1.43 $element_state->{has_element} = 1;
5146 wakaba 1.40 } elsif ($element_state->{phase} eq 'any') {
5147     if ($child_nsuri eq $HTML_NS and
5148     {table => 1, select => 1, datalist => 1}->{$child_ln}) {
5149     $element_state->{phase} = 'none';
5150 wakaba 1.72 } elsif ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) {
5151 wakaba 1.40 $element_state->{has_element} = 1;
5152 wakaba 1.72 $element_state->{phase} = 'flow';
5153 wakaba 1.43 ## TODO: transparent?
5154 wakaba 1.40 } else {
5155     $self->{onerror}->(node => $child_el,
5156     type => 'element not allowed');
5157     }
5158     } elsif ($element_state->{phase} eq 'none') {
5159     $self->{onerror}->(node => $child_el,
5160     type => 'element not allowed');
5161     } else {
5162     die "check_child_element: Bad |datagrid| phase: $element_state->{phase}";
5163     }
5164     },
5165     check_child_text => sub {
5166     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5167     if ($has_significant) {
5168 wakaba 1.72 if ($element_state->{phase} eq 'flow') {
5169 wakaba 1.40 #
5170     } elsif ($element_state->{phase} eq 'any') {
5171 wakaba 1.72 $element_state->{phase} = 'flow';
5172 wakaba 1.40 } else {
5173     $self->{onerror}->(node => $child_node,
5174     type => 'character not allowed');
5175 wakaba 1.1 }
5176     }
5177 wakaba 1.40 },
5178     check_end => sub {
5179     my ($self, $item, $element_state) = @_;
5180     $self->_remove_minus_elements ($element_state);
5181 wakaba 1.1
5182 wakaba 1.40 if ($element_state->{phase} eq 'none') {
5183     $HTMLChecker{check_end}->(@_);
5184     } else {
5185     $HTMLPhrasingContentChecker{check_end}->(@_);
5186     }
5187     },
5188 wakaba 1.29 ## ISSUE: "xxx<table/>" is disallowed; "<select/>aaa" and "<datalist/>aa"
5189     ## are not disallowed (assuming that form control contents are also
5190 wakaba 1.72 ## flow content).
5191 wakaba 1.1 };
5192    
5193     $Element->{$HTML_NS}->{command} = {
5194 wakaba 1.40 %HTMLEmptyChecker,
5195 wakaba 1.48 status => FEATURE_HTML5_WD,
5196 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5197 wakaba 1.1 checked => $GetHTMLBooleanAttrChecker->('checked'),
5198     default => $GetHTMLBooleanAttrChecker->('default'),
5199     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5200     hidden => $GetHTMLBooleanAttrChecker->('hidden'),
5201     icon => $HTMLURIAttrChecker,
5202     label => sub { }, ## NOTE: No conformance creteria
5203     radiogroup => sub { }, ## NOTE: No conformance creteria
5204     type => sub {
5205     my ($self, $attr) = @_;
5206     my $value = $attr->value;
5207     unless ({command => 1, checkbox => 1, radio => 1}->{$value}) {
5208     $self->{onerror}->(node => $attr, type => 'attribute value not allowed');
5209     }
5210     },
5211 wakaba 1.50 }, {
5212     %HTMLAttrStatus,
5213     checked => FEATURE_HTML5_WD,
5214     default => FEATURE_HTML5_WD,
5215     disabled => FEATURE_HTML5_WD,
5216     hidden => FEATURE_HTML5_WD,
5217     icon => FEATURE_HTML5_WD,
5218     label => FEATURE_HTML5_WD,
5219     radiogroup => FEATURE_HTML5_WD,
5220     type => FEATURE_HTML5_WD,
5221 wakaba 1.1 }),
5222 wakaba 1.66 check_start => sub {
5223     my ($self, $item, $element_state) = @_;
5224    
5225     $element_state->{uri_info}->{icon}->{type}->{embedded} = 1;
5226 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5227     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5228 wakaba 1.66 },
5229 wakaba 1.1 };
5230    
5231     $Element->{$HTML_NS}->{menu} = {
5232 wakaba 1.40 %HTMLPhrasingContentChecker,
5233 wakaba 1.54 #status => FEATURE_M12N10_REC_DEPRECATED | FEATURE_HTML5_WD,
5234     status => FEATURE_M12N10_REC | FEATURE_HTML5_WD,
5235     ## NOTE: We don't want any |menu| element warned as deprecated.
5236 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5237 wakaba 1.1 autosubmit => $GetHTMLBooleanAttrChecker->('autosubmit'),
5238 wakaba 1.68 compact => $GetHTMLBooleanAttrChecker->('compact'),
5239 wakaba 1.1 id => sub {
5240     ## NOTE: same as global |id=""|, with |$self->{menu}| registeration
5241     my ($self, $attr) = @_;
5242     my $value = $attr->value;
5243     if (length $value > 0) {
5244     if ($self->{id}->{$value}) {
5245     $self->{onerror}->(node => $attr, type => 'duplicate ID');
5246     push @{$self->{id}->{$value}}, $attr;
5247     } else {
5248     $self->{id}->{$value} = [$attr];
5249     }
5250     } else {
5251     ## NOTE: MUST contain at least one character
5252     $self->{onerror}->(node => $attr, type => 'empty attribute value');
5253     }
5254     if ($value =~ /[\x09-\x0D\x20]/) {
5255     $self->{onerror}->(node => $attr, type => 'space in ID');
5256     }
5257     $self->{menu}->{$value} ||= $attr;
5258     ## ISSUE: <menu id=""><p contextmenu=""> match?
5259     },
5260     label => sub { }, ## NOTE: No conformance creteria
5261     type => $GetHTMLEnumeratedAttrChecker->({context => 1, toolbar => 1}),
5262 wakaba 1.49 }, {
5263     %HTMLAttrStatus,
5264     %HTMLM12NCommonAttrStatus,
5265 wakaba 1.61 align => FEATURE_HTML2X_RFC,
5266 wakaba 1.50 autosubmit => FEATURE_HTML5_WD,
5267 wakaba 1.49 compat => FEATURE_M12N10_REC_DEPRECATED,
5268 wakaba 1.50 label => FEATURE_HTML5_WD,
5269     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5270 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
5271     sdapref => FEATURE_HTML20_RFC,
5272 wakaba 1.50 type => FEATURE_HTML5_WD,
5273 wakaba 1.1 }),
5274 wakaba 1.40 check_start => sub {
5275     my ($self, $item, $element_state) = @_;
5276     $element_state->{phase} = 'li or phrasing';
5277     $element_state->{in_menu_original} = $self->{flag}->{in_menu};
5278     $self->{flag}->{in_menu} = 1;
5279 wakaba 1.79
5280     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5281     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5282 wakaba 1.40 },
5283     check_child_element => sub {
5284     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5285     $child_is_transparent, $element_state) = @_;
5286     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5287     $self->{onerror}->(node => $child_el,
5288     type => 'element not allowed:minus',
5289     level => $self->{must_level});
5290     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5291     #
5292     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'li') {
5293     if ($element_state->{phase} eq 'li') {
5294     #
5295     } elsif ($element_state->{phase} eq 'li or phrasing') {
5296     $element_state->{phase} = 'li';
5297     } else {
5298     $self->{onerror}->(node => $child_el, type => 'element not allowed');
5299     }
5300     } elsif ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
5301     if ($element_state->{phase} eq 'phrasing') {
5302     #
5303     } elsif ($element_state->{phase} eq 'li or phrasing') {
5304     $element_state->{phase} = 'phrasing';
5305     } else {
5306     $self->{onerror}->(node => $child_el, type => 'element not allowed');
5307     }
5308     } else {
5309     $self->{onerror}->(node => $child_el, type => 'element not allowed');
5310     }
5311     },
5312     check_child_text => sub {
5313     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5314     if ($has_significant) {
5315     if ($element_state->{phase} eq 'phrasing') {
5316     #
5317     } elsif ($element_state->{phase} eq 'li or phrasing') {
5318     $element_state->{phase} = 'phrasing';
5319     } else {
5320     $self->{onerror}->(node => $child_node,
5321     type => 'character not allowed');
5322 wakaba 1.1 }
5323     }
5324 wakaba 1.40 },
5325     check_end => sub {
5326     my ($self, $item, $element_state) = @_;
5327     delete $self->{flag}->{in_menu} unless $element_state->{in_menu_original};
5328    
5329     if ($element_state->{phase} eq 'li') {
5330     $HTMLChecker{check_end}->(@_);
5331     } else { # 'phrasing' or 'li or phrasing'
5332     $HTMLPhrasingContentChecker{check_end}->(@_);
5333 wakaba 1.1 }
5334     },
5335 wakaba 1.8 };
5336    
5337     $Element->{$HTML_NS}->{datatemplate} = {
5338 wakaba 1.40 %HTMLChecker,
5339 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
5340 wakaba 1.40 check_child_element => sub {
5341     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5342     $child_is_transparent, $element_state) = @_;
5343     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5344     $self->{onerror}->(node => $child_el,
5345     type => 'element not allowed:minus',
5346     level => $self->{must_level});
5347     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5348     #
5349     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'rule') {
5350     #
5351     } else {
5352     $self->{onerror}->(node => $child_el,
5353     type => 'element not allowed:datatemplate');
5354     }
5355     },
5356     check_child_text => sub {
5357     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5358     if ($has_significant) {
5359     $self->{onerror}->(node => $child_node, type => 'character not allowed');
5360 wakaba 1.8 }
5361     },
5362     is_xml_root => 1,
5363     };
5364    
5365     $Element->{$HTML_NS}->{rule} = {
5366 wakaba 1.40 %HTMLChecker,
5367 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
5368 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5369 wakaba 1.23 condition => $HTMLSelectorsAttrChecker,
5370 wakaba 1.18 mode => $HTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker,
5371 wakaba 1.50 }, {
5372     %HTMLAttrStatus,
5373     condition => FEATURE_HTML5_AT_RISK,
5374     mode => FEATURE_HTML5_AT_RISK,
5375 wakaba 1.8 }),
5376 wakaba 1.40 check_start => sub {
5377     my ($self, $item, $element_state) = @_;
5378 wakaba 1.79
5379 wakaba 1.40 $self->_add_plus_elements ($element_state, {$HTML_NS => {nest => 1}});
5380 wakaba 1.79 $element_state->{in_rule_original} = $self->{flag}->{in_rule};
5381     $self->{flag}->{in_rule} = 1;
5382    
5383     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5384     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5385 wakaba 1.40 },
5386     check_child_element => sub { },
5387     check_child_text => sub { },
5388     check_end => sub {
5389     my ($self, $item, $element_state) = @_;
5390 wakaba 1.79
5391 wakaba 1.40 $self->_remove_plus_elements ($element_state);
5392 wakaba 1.79 delete $self->{flag}->{in_rule} unless $element_state->{in_rule_original};
5393    
5394 wakaba 1.40 $HTMLChecker{check_end}->(@_);
5395 wakaba 1.8 },
5396     ## NOTE: "MAY be anything that, when the parent |datatemplate|
5397     ## is applied to some conforming data, results in a conforming DOM tree.":
5398     ## We don't check against this.
5399     };
5400    
5401     $Element->{$HTML_NS}->{nest} = {
5402 wakaba 1.40 %HTMLEmptyChecker,
5403 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
5404 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5405 wakaba 1.23 filter => $HTMLSelectorsAttrChecker,
5406     mode => sub {
5407     my ($self, $attr) = @_;
5408     my $value = $attr->value;
5409     if ($value !~ /\A[^\x09-\x0D\x20]+\z/) {
5410     $self->{onerror}->(node => $attr, type => 'mode:syntax error');
5411     }
5412     },
5413 wakaba 1.50 }, {
5414     %HTMLAttrStatus,
5415     filter => FEATURE_HTML5_AT_RISK,
5416     mode => FEATURE_HTML5_AT_RISK,
5417 wakaba 1.8 }),
5418 wakaba 1.1 };
5419    
5420     $Element->{$HTML_NS}->{legend} = {
5421 wakaba 1.40 %HTMLPhrasingContentChecker,
5422 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5423 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5424 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5425 wakaba 1.52 # align => $GetHTMLEnumeratedAttrChecker->({
5426     # top => 1, bottom => 1, left => 1, right => 1,
5427     # }),
5428     }, {
5429 wakaba 1.49 %HTMLAttrStatus,
5430     %HTMLM12NCommonAttrStatus,
5431     accesskey => FEATURE_M12N10_REC,
5432     align => FEATURE_M12N10_REC_DEPRECATED,
5433 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5434 wakaba 1.49 }),
5435 wakaba 1.1 };
5436    
5437     $Element->{$HTML_NS}->{div} = {
5438 wakaba 1.72 %HTMLFlowContentChecker,
5439 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5440 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
5441     align => $GetHTMLEnumeratedAttrChecker->({
5442     left => 1, center => 1, right => 1, justify => 1,
5443     }),
5444     }, {
5445 wakaba 1.49 %HTMLAttrStatus,
5446 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5447 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
5448     datafld => FEATURE_HTML4_REC_RESERVED,
5449     dataformatas => FEATURE_HTML4_REC_RESERVED,
5450     datasrc => FEATURE_HTML4_REC_RESERVED,
5451 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5452 wakaba 1.49 }),
5453 wakaba 1.66 check_start => sub {
5454     my ($self, $item, $element_state) = @_;
5455    
5456     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
5457 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5458     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5459 wakaba 1.66 },
5460 wakaba 1.1 };
5461    
5462 wakaba 1.64 $Element->{$HTML_NS}->{center} = {
5463 wakaba 1.72 %HTMLFlowContentChecker,
5464 wakaba 1.64 status => FEATURE_M12N10_REC_DEPRECATED,
5465     check_attrs => $GetHTMLAttrsChecker->({}, {
5466     %HTMLAttrStatus,
5467     %HTMLM12NCommonAttrStatus,
5468     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5469     }),
5470     };
5471    
5472 wakaba 1.1 $Element->{$HTML_NS}->{font} = {
5473 wakaba 1.40 %HTMLTransparentChecker,
5474 wakaba 1.78 status => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC_DEPRECATED,
5475 wakaba 1.70 check_attrs => $GetHTMLAttrsChecker->({
5476     ## TODO: HTML4 |size|, |color|, |face|
5477 wakaba 1.49 }, {
5478     %HTMLAttrStatus,
5479 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5480 wakaba 1.49 color => FEATURE_M12N10_REC_DEPRECATED,
5481 wakaba 1.50 dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5482 wakaba 1.49 face => FEATURE_M12N10_REC_DEPRECATED,
5483 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5484     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5485 wakaba 1.49 size => FEATURE_M12N10_REC_DEPRECATED,
5486 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5487 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5488 wakaba 1.49 }),
5489 wakaba 1.78 ## NOTE: When the |font| element was defined in the HTML5 specification,
5490     ## it is allowed only in a document with the WYSIWYG signature. The
5491     ## checker does not check whether there is the signature, since the
5492     ## signature is dropped, too, and has never been implemented. (In addition,
5493     ## for any |font| element an "element not defined" error is raised anyway,
5494     ## such that we don't have to raise an additional error.)
5495 wakaba 1.1 };
5496 wakaba 1.49
5497 wakaba 1.64 $Element->{$HTML_NS}->{basefont} = {
5498     %HTMLEmptyChecker,
5499     status => FEATURE_M12N10_REC_DEPRECATED,
5500     check_attrs => $GetHTMLAttrsChecker->({
5501     ## TODO: color, face, size
5502     }, {
5503     %HTMLAttrStatus,
5504     color => FEATURE_M12N10_REC_DEPRECATED,
5505     face => FEATURE_M12N10_REC_DEPRECATED,
5506     #id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
5507     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5508     size => FEATURE_M12N10_REC_DEPRECATED,
5509     }),
5510     };
5511    
5512 wakaba 1.49 ## TODO: frameset FEATURE_M12N10_REC
5513     ## class title id cols rows onload onunload style(x10)
5514     ## frame frameborder longdesc marginheight marginwidth noresize scrolling src name(deprecated) class,id,title,style(x10)
5515     ## noframes Common, lang(xhtml10)
5516    
5517 wakaba 1.82 ## TODO: CR: ruby rb rt rp rbc rtc @rbspan (M12NXHTML2Common)
5518 wakaba 1.56
5519 wakaba 1.61 ## TODO: xmp, listing, plaintext FEATURE_HTML32_REC_OBSOLETE
5520     ## TODO: ^^^ lang, dir, id, class [HTML 2.x] sdaform [HTML 2.0]
5521     ## xmp, listing sdapref[HTML2,0]
5522    
5523 wakaba 1.56 =pod
5524    
5525     WF2: Documents MUST comply to [CHARMOD].
5526     WF2: Vencor extensions MUST NOT be used.
5527    
5528 wakaba 1.61 HTML 2.0 nextid @n
5529    
5530     RFC 2659: CERTS CRYPTOPTS
5531    
5532     ISO-HTML: pre-html, divN
5533 wakaba 1.82
5534     XHTML2: blockcode (Common), h (Common), separator (Common), l (Common),
5535     di (Common), nl (Common), handler (Common, type), standby (Common),
5536     summary (Common)
5537    
5538     Access & XHTML2: access
5539    
5540     XML Events & XForms (for XHTML2 support; very, very low priority)
5541 wakaba 1.61
5542 wakaba 1.56 =cut
5543 wakaba 1.61
5544     ## NOTE: Where RFC 2659 allows additional attributes is unclear.
5545     ## We added them only to |a|. |link| and |form| might also allow them
5546     ## in theory.
5547 wakaba 1.1
5548     $Whatpm::ContentChecker::Namespace->{$HTML_NS}->{loaded} = 1;
5549    
5550     1;

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24