/[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.87 - (hide annotations) (download)
Tue May 6 08:59:09 2008 UTC (16 years, 6 months ago) by wakaba
Branch: MAIN
Changes since 1.86: +13 -6 lines
++ whatpm/t/ChangeLog	6 May 2008 08:59:04 -0000
2008-05-06  Wakaba  <wakaba@suika.fam.cx>

	* content-model-2.dat: Test data for td/@headers are added.

++ whatpm/Whatpm/ChangeLog	6 May 2008 08:57:07 -0000
	* ContentChecker.pm: Noted that those returned in |table| are
	no longer table elements, but table objects returned
	by Whatpm::HTMLTable.

	* HTMLTable.pm (form_table): Return table element node
	as |$table->{element}|.
	(assign_header): Support for the |headers=""| attribute.

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	6 May 2008 08:58:42 -0000
2008-05-06  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: Invoke |Whatpm::HTMLTable->assign_header| for each
	table object.  Return the table object, not table element.
	The |headers=""| checker for |td| elements are now noop.
	Set the status of |headers=""| attribute as HTML5's one.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24