/[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.70 - (hide annotations) (download)
Sat Mar 22 11:40:16 2008 UTC (17 years, 4 months ago) by wakaba
Branch: MAIN
Changes since 1.69: +68 -5 lines
++ whatpm/Whatpm/ContentChecker/ChangeLog	22 Mar 2008 11:37:19 -0000
	* HTML.pm: link/@target, img/@align, img/@border, img/@hspace,
	img/@vspace, img/@longdesc, object/@align, object/@archive,
	object/@border, object/@classid, object/@codebase,
	object/@codetype, object/@declare, object/@hspace,
	object/@standby, object/@vspace, param/@valuetype,
	param/@type, area/@nohref, %cellhalign/@align, %cellhalign/@char,
	and %cellvalign/@valign are implemented.

2008-03-22  Wakaba  <wakaba@suika.fam.cx>

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24