/[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.88 - (hide annotations) (download)
Sat May 10 06:04:39 2008 UTC (17 years, 2 months ago) by wakaba
Branch: MAIN
Changes since 1.87: +3 -2 lines
++ whatpm/t/ChangeLog	10 May 2008 05:54:08 -0000
2008-05-10  Wakaba  <wakaba@suika.fam.cx>

	* content-model-2.dat: Boolean attributes now allow uppercase
	letters (HTML5 revision 1572).

++ whatpm/Whatpm/ChangeLog	10 May 2008 05:57:19 -0000
2008-05-10  Wakaba  <wakaba@suika.fam.cx>

	* CacheManifest.pm: Don't replace U+0000 NULL (HTML5 revision
	1553).

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

	* HTML.pm ($GetHTMLBooleanAttrChecker): Allow uppercase
	letters (HTML5 revision 1572).

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24