/[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.86 - (hide annotations) (download)
Mon May 5 11:15:13 2008 UTC (16 years, 6 months ago) by wakaba
Branch: MAIN
Changes since 1.85: +20 -6 lines
++ whatpm/t/ChangeLog	5 May 2008 11:15:04 -0000
	* content-model-2.dat: Test data for table/@cellpadding,
	table/@cellspacing, @charoff, and script/@langauge are added.

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	5 May 2008 11:14:32 -0000
	* HTML.pm ($HTMLLengthAttrChecker): New.
	(table/@cellpadding, table/@cellspacing, @charoff): Implemented.
	(script/@langauge): Implemented.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24