/[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.83 - (hide annotations) (download)
Mon May 5 04:00:39 2008 UTC (16 years, 6 months ago) by wakaba
Branch: MAIN
Changes since 1.82: +39 -3 lines
++ whatpm/t/ChangeLog	5 May 2008 03:59:40 -0000
2008-05-05  Wakaba  <wakaba@suika.fam.cx>

	* content-model-2.dat: Test data for repeat* global attributes
	are added.

++ whatpm/Whatpm/ContentChecker/ChangeLog	5 May 2008 04:00:12 -0000
2008-05-05  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: Raise an error if a repeat* global attrbute
	is used for an HTML element.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24