/[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.71 - (hide annotations) (download)
Sat Mar 22 12:29:59 2008 UTC (16 years, 7 months ago) by wakaba
Branch: MAIN
Changes since 1.70: +3 -12 lines
++ whatpm/t/ChangeLog	22 Mar 2008 12:23:33 -0000
	* content-model-2.dat: Test data on |rev| attribute are added.

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	22 Mar 2008 12:23:50 -0000
	* HTML.pm: link/@rev and a/@rev 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.71 rev => $HTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker,
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 wakaba 1.71 ## NOTE: <http://suika.fam.cx/2007/html/standards#html-meta-scheme>
1305 wakaba 1.67 $checker = sub {};
1306 wakaba 1.1 } else {
1307     $checker = $HTMLAttrChecker->{$attr_ln}
1308 wakaba 1.67 || $AttrChecker->{$attr_ns}->{$attr_ln}
1309 wakaba 1.1 || $AttrChecker->{$attr_ns}->{''};
1310     }
1311     } else {
1312     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
1313     || $AttrChecker->{$attr_ns}->{''};
1314     }
1315 wakaba 1.62
1316     my $status = {
1317     %HTMLAttrStatus,
1318     charset => FEATURE_HTML5_DEFAULT,
1319     content => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1320     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1321     'http-equiv' => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1322     id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1323     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1324     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1325     scheme => FEATURE_M12N10_REC,
1326     }->{$attr_ln};
1327    
1328 wakaba 1.1 if ($checker) {
1329 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
1330 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
1331 wakaba 1.54 #
1332 wakaba 1.1 } else {
1333     $self->{onerror}->(node => $attr, level => 'unsupported',
1334     type => 'attribute');
1335 wakaba 1.49 ## ISSUE: No conformance createria for unknown attributes in the spec
1336     }
1337    
1338     if ($attr_ns eq '') {
1339 wakaba 1.62 $self->_attr_status_info ($attr, $status);
1340 wakaba 1.1 }
1341     }
1342    
1343     if (defined $name_attr) {
1344     if (defined $http_equiv_attr) {
1345     $self->{onerror}->(node => $http_equiv_attr,
1346     type => 'attribute not allowed');
1347     } elsif (defined $charset_attr) {
1348     $self->{onerror}->(node => $charset_attr,
1349     type => 'attribute not allowed');
1350     }
1351     my $metadata_name = $name_attr->value;
1352     my $metadata_value;
1353     if (defined $content_attr) {
1354     $metadata_value = $content_attr->value;
1355     } else {
1356 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1357 wakaba 1.1 type => 'attribute missing:content');
1358     $metadata_value = '';
1359     }
1360     } elsif (defined $http_equiv_attr) {
1361     if (defined $charset_attr) {
1362     $self->{onerror}->(node => $charset_attr,
1363     type => 'attribute not allowed');
1364     }
1365     unless (defined $content_attr) {
1366 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1367 wakaba 1.1 type => 'attribute missing:content');
1368     }
1369     } elsif (defined $charset_attr) {
1370     if (defined $content_attr) {
1371     $self->{onerror}->(node => $content_attr,
1372     type => 'attribute not allowed');
1373     }
1374     } else {
1375     if (defined $content_attr) {
1376     $self->{onerror}->(node => $content_attr,
1377     type => 'attribute not allowed');
1378 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1379 wakaba 1.1 type => 'attribute missing:name|http-equiv');
1380     } else {
1381 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1382 wakaba 1.1 type => 'attribute missing:name|http-equiv|charset');
1383     }
1384     }
1385    
1386 wakaba 1.32 my $check_charset_decl = sub () {
1387 wakaba 1.40 my $parent = $item->{node}->manakai_parent_element;
1388 wakaba 1.29 if ($parent and $parent eq $parent->owner_document->manakai_head) {
1389     for my $el (@{$parent->child_nodes}) {
1390     next unless $el->node_type == 1; # ELEMENT_NODE
1391 wakaba 1.40 unless ($el eq $item->{node}) {
1392 wakaba 1.29 ## NOTE: Not the first child element.
1393 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1394 wakaba 1.32 type => 'element not allowed:meta charset',
1395     level => $self->{must_level});
1396 wakaba 1.29 }
1397     last;
1398     ## NOTE: Entity references are not supported.
1399     }
1400     } else {
1401 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1402 wakaba 1.32 type => 'element not allowed:meta charset',
1403     level => $self->{must_level});
1404 wakaba 1.29 }
1405    
1406 wakaba 1.40 unless ($item->{node}->owner_document->manakai_is_html) {
1407     $self->{onerror}->(node => $item->{node},
1408 wakaba 1.32 type => 'in XML:charset',
1409     level => $self->{must_level});
1410 wakaba 1.1 }
1411 wakaba 1.32 }; # $check_charset_decl
1412 wakaba 1.21
1413 wakaba 1.32 my $check_charset = sub ($$) {
1414     my ($attr, $charset_value) = @_;
1415 wakaba 1.21 ## NOTE: Though the case-sensitivility of |charset| attribute value
1416     ## is not explicitly spelled in the HTML5 spec, the Character Set
1417     ## registry of IANA, which is referenced from HTML5 spec, says that
1418     ## charset name is case-insensitive.
1419     $charset_value =~ tr/A-Z/a-z/; ## NOTE: ASCII Case-insensitive.
1420    
1421     require Message::Charset::Info;
1422     my $charset = $Message::Charset::Info::IANACharset->{$charset_value};
1423 wakaba 1.40 my $ic = $item->{node}->owner_document->input_encoding;
1424 wakaba 1.21 if (defined $ic) {
1425     ## TODO: Test for this case
1426     my $ic_charset = $Message::Charset::Info::IANACharset->{$ic};
1427     if ($charset ne $ic_charset) {
1428 wakaba 1.32 $self->{onerror}->(node => $attr,
1429 wakaba 1.21 type => 'mismatched charset name:'.$ic.
1430 wakaba 1.32 ':'.$charset_value, ## TODO: This should be a |value| value.
1431     level => $self->{must_level});
1432 wakaba 1.21 }
1433     } else {
1434     ## NOTE: MUST, but not checkable, since the document is not originally
1435     ## in serialized form (or the parser does not preserve the input
1436     ## encoding information).
1437 wakaba 1.32 $self->{onerror}->(node => $attr,
1438     type => 'mismatched charset name::'.$charset_value, ## TODO: |value|
1439 wakaba 1.21 level => 'unsupported');
1440     }
1441    
1442     ## ISSUE: What is "valid character encoding name"? Syntactically valid?
1443     ## Syntactically valid and registered? What about x-charset names?
1444     unless (Message::Charset::Info::is_syntactically_valid_iana_charset_name
1445     ($charset_value)) {
1446 wakaba 1.32 $self->{onerror}->(node => $attr,
1447     type => 'charset:syntax error:'.$charset_value, ## TODO
1448     level => $self->{must_level});
1449 wakaba 1.21 }
1450    
1451     if ($charset) {
1452     ## ISSUE: What is "the preferred name for that encoding" (for a charset
1453     ## with no "preferred MIME name" label)?
1454     my $charset_status = $charset->{iana_names}->{$charset_value} || 0;
1455     if (($charset_status &
1456     Message::Charset::Info::PREFERRED_CHARSET_NAME ())
1457     != Message::Charset::Info::PREFERRED_CHARSET_NAME ()) {
1458 wakaba 1.32 $self->{onerror}->(node => $attr,
1459 wakaba 1.21 type => 'charset:not preferred:'.
1460 wakaba 1.32 $charset_value, ## TODO
1461     level => $self->{must_level});
1462 wakaba 1.21 }
1463     if (($charset_status &
1464     Message::Charset::Info::REGISTERED_CHARSET_NAME ())
1465     != Message::Charset::Info::REGISTERED_CHARSET_NAME ()) {
1466     if ($charset_value =~ /^x-/) {
1467 wakaba 1.32 $self->{onerror}->(node => $attr,
1468     type => 'charset:private:'.$charset_value, ## TODO
1469 wakaba 1.21 level => $self->{good_level});
1470     } else {
1471 wakaba 1.32 $self->{onerror}->(node => $attr,
1472 wakaba 1.21 type => 'charset:not registered:'.
1473 wakaba 1.32 $charset_value, ## TODO
1474 wakaba 1.21 level => $self->{good_level});
1475     }
1476     }
1477     } elsif ($charset_value =~ /^x-/) {
1478 wakaba 1.32 $self->{onerror}->(node => $attr,
1479     type => 'charset:private:'.$charset_value, ## TODO
1480 wakaba 1.21 level => $self->{good_level});
1481     } else {
1482 wakaba 1.32 $self->{onerror}->(node => $attr,
1483     type => 'charset:not registered:'.$charset_value, ## TODO
1484 wakaba 1.21 level => $self->{good_level});
1485     }
1486    
1487 wakaba 1.32 if ($attr->get_user_data ('manakai_has_reference')) {
1488     $self->{onerror}->(node => $attr,
1489 wakaba 1.22 type => 'character reference in charset',
1490     level => $self->{must_level});
1491     }
1492 wakaba 1.32 }; # $check_charset
1493    
1494     ## TODO: metadata conformance
1495    
1496     ## TODO: pragma conformance
1497     if (defined $http_equiv_attr) { ## An enumerated attribute
1498     my $keyword = lc $http_equiv_attr->value; ## TODO: ascii case?
1499     if ({
1500     'refresh' => 1,
1501     'default-style' => 1,
1502     }->{$keyword}) {
1503     #
1504 wakaba 1.33
1505     ## TODO: More than one occurence is a MUST-level error (revision 1180).
1506 wakaba 1.32 } elsif ($keyword eq 'content-type') {
1507 wakaba 1.58 ## TODO: refs in "text/html; charset=" are not disallowed since rev.1275.
1508 wakaba 1.33
1509 wakaba 1.32 $check_charset_decl->();
1510     if ($content_attr) {
1511     my $content = $content_attr->value;
1512 wakaba 1.58 if ($content =~ m!^[Tt][Ee][Xx][Tt]/[Hh][Tt][Mm][Ll];
1513     [\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
1514     =(.+)\z!sx) {
1515 wakaba 1.32 $check_charset->($content_attr, $1);
1516     } else {
1517     $self->{onerror}->(node => $content_attr,
1518     type => 'meta content-type syntax error',
1519     level => $self->{must_level});
1520     }
1521     }
1522     } else {
1523     $self->{onerror}->(node => $http_equiv_attr,
1524     type => 'enumerated:invalid');
1525     }
1526     }
1527    
1528     if (defined $charset_attr) {
1529     $check_charset_decl->();
1530     $check_charset->($charset_attr, $charset_attr->value);
1531 wakaba 1.1 }
1532     },
1533     };
1534    
1535     $Element->{$HTML_NS}->{style} = {
1536 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1537 wakaba 1.40 %HTMLChecker,
1538     check_attrs => $GetHTMLAttrsChecker->({
1539 wakaba 1.1 type => $HTMLIMTAttrChecker, ## TODO: MUST be a styling language
1540     media => $HTMLMQAttrChecker,
1541     scoped => $GetHTMLBooleanAttrChecker->('scoped'),
1542     ## NOTE: |title| has special semantics for |style|s, but is syntactically
1543     ## not different
1544 wakaba 1.49 }, {
1545     %HTMLAttrStatus,
1546 wakaba 1.50 dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1547     id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1548     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1549     media => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1550     scoped => FEATURE_HTML5_DEFAULT,
1551     title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1552     type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1553 wakaba 1.1 }),
1554 wakaba 1.40 check_start => sub {
1555     my ($self, $item, $element_state) = @_;
1556    
1557 wakaba 1.27 ## NOTE: |html:style| itself has no conformance creteria on content model.
1558 wakaba 1.40 my $type = $item->{node}->get_attribute_ns (undef, 'type');
1559 wakaba 1.27 if (not defined $type or
1560     $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]) {
1561 wakaba 1.40 $element_state->{allow_element} = 0;
1562     $element_state->{style_type} = 'text/css';
1563     } else {
1564     $element_state->{allow_element} = 1; # unknown
1565     $element_state->{style_type} = $type; ## TODO: $type normalization
1566     }
1567     },
1568     check_child_element => sub {
1569     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1570     $child_is_transparent, $element_state) = @_;
1571     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
1572     $self->{onerror}->(node => $child_el,
1573     type => 'element not allowed:minus',
1574     level => $self->{must_level});
1575     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1576     #
1577     } elsif ($element_state->{allow_element}) {
1578     #
1579     } else {
1580     $self->{onerror}->(node => $child_el, type => 'element not allowed');
1581     }
1582     },
1583     check_child_text => sub {
1584     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1585     $element_state->{text} .= $child_node->text_content;
1586     },
1587     check_end => sub {
1588     my ($self, $item, $element_state) = @_;
1589     if ($element_state->{style_type} eq 'text/css') {
1590     $self->{onsubdoc}->({s => $element_state->{text},
1591     container_node => $item->{node},
1592 wakaba 1.28 media_type => 'text/css', is_char_string => 1});
1593 wakaba 1.27 } else {
1594 wakaba 1.40 $self->{onerror}->(node => $item->{node}, level => 'unsupported',
1595     type => 'style:'.$element_state->{style_type});
1596 wakaba 1.27 }
1597 wakaba 1.40
1598     $HTMLChecker{check_end}->(@_);
1599 wakaba 1.1 },
1600     };
1601 wakaba 1.25 ## ISSUE: Relationship to significant content check?
1602 wakaba 1.1
1603     $Element->{$HTML_NS}->{body} = {
1604 wakaba 1.40 %HTMLProseContentChecker,
1605 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1606 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
1607     alink => $HTMLColorAttrChecker,
1608     background => $HTMLURIAttrChecker,
1609     bgcolor => $HTMLColorAttrChecker,
1610     link => $HTMLColorAttrChecker,
1611     text => $HTMLColorAttrChecker,
1612     vlink => $HTMLColorAttrChecker,
1613     }, {
1614 wakaba 1.49 %HTMLAttrStatus,
1615     %HTMLM12NCommonAttrStatus,
1616     alink => FEATURE_M12N10_REC_DEPRECATED,
1617     background => FEATURE_M12N10_REC_DEPRECATED,
1618     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
1619 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1620 wakaba 1.49 link => FEATURE_M12N10_REC_DEPRECATED,
1621 wakaba 1.50 onload => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1622     onunload => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1623 wakaba 1.49 text => FEATURE_M12N10_REC_DEPRECATED,
1624     vlink => FEATURE_M12N10_REC_DEPRECATED,
1625     }),
1626 wakaba 1.68 check_start => sub {
1627     my ($self, $item, $element_state) = @_;
1628    
1629     $element_state->{uri_info}->{background}->{type}->{embedded} = 1;
1630     },
1631 wakaba 1.1 };
1632    
1633     $Element->{$HTML_NS}->{section} = {
1634 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
1635 wakaba 1.40 %HTMLProseContentChecker,
1636 wakaba 1.1 };
1637    
1638     $Element->{$HTML_NS}->{nav} = {
1639 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
1640 wakaba 1.40 %HTMLProseContentChecker,
1641 wakaba 1.1 };
1642    
1643     $Element->{$HTML_NS}->{article} = {
1644 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
1645 wakaba 1.40 %HTMLProseContentChecker,
1646 wakaba 1.1 };
1647    
1648     $Element->{$HTML_NS}->{blockquote} = {
1649 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1650 wakaba 1.40 %HTMLProseContentChecker,
1651     check_attrs => $GetHTMLAttrsChecker->({
1652 wakaba 1.1 cite => $HTMLURIAttrChecker,
1653 wakaba 1.49 }, {
1654     %HTMLAttrStatus,
1655     %HTMLM12NCommonAttrStatus,
1656 wakaba 1.61 align => FEATURE_HTML2X_RFC,
1657 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1658     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1659 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
1660 wakaba 1.1 }),
1661 wakaba 1.66 check_start => sub {
1662     my ($self, $item, $element_state) = @_;
1663    
1664     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
1665     },
1666 wakaba 1.1 };
1667    
1668     $Element->{$HTML_NS}->{aside} = {
1669 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
1670 wakaba 1.40 %HTMLProseContentChecker,
1671 wakaba 1.1 };
1672    
1673     $Element->{$HTML_NS}->{h1} = {
1674 wakaba 1.40 %HTMLPhrasingContentChecker,
1675 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1676 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
1677     align => $GetHTMLEnumeratedAttrChecker->({
1678     left => 1, center => 1, right => 1, justify => 1,
1679     }),
1680     }, {
1681 wakaba 1.49 %HTMLAttrStatus,
1682     %HTMLM12NCommonAttrStatus,
1683     align => FEATURE_M12N10_REC_DEPRECATED,
1684 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1685 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
1686 wakaba 1.49 }),
1687 wakaba 1.40 check_start => sub {
1688     my ($self, $item, $element_state) = @_;
1689     $self->{flag}->{has_hn} = 1;
1690 wakaba 1.1 },
1691     };
1692    
1693 wakaba 1.40 $Element->{$HTML_NS}->{h2} = {%{$Element->{$HTML_NS}->{h1}}};
1694 wakaba 1.1
1695 wakaba 1.40 $Element->{$HTML_NS}->{h3} = {%{$Element->{$HTML_NS}->{h1}}};
1696 wakaba 1.1
1697 wakaba 1.40 $Element->{$HTML_NS}->{h4} = {%{$Element->{$HTML_NS}->{h1}}};
1698 wakaba 1.1
1699 wakaba 1.40 $Element->{$HTML_NS}->{h5} = {%{$Element->{$HTML_NS}->{h1}}};
1700 wakaba 1.1
1701 wakaba 1.40 $Element->{$HTML_NS}->{h6} = {%{$Element->{$HTML_NS}->{h1}}};
1702 wakaba 1.1
1703 wakaba 1.29 ## TODO: Explicit sectioning is "encouraged".
1704    
1705 wakaba 1.1 $Element->{$HTML_NS}->{header} = {
1706 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
1707 wakaba 1.40 %HTMLProseContentChecker,
1708     check_start => sub {
1709     my ($self, $item, $element_state) = @_;
1710     $self->_add_minus_elements ($element_state,
1711     {$HTML_NS => {qw/header 1 footer 1/}},
1712 wakaba 1.58 $HTMLSectioningContent);
1713 wakaba 1.40 $element_state->{has_hn_original} = $self->{flag}->{has_hn};
1714     $self->{flag}->{has_hn} = 0;
1715     },
1716     check_end => sub {
1717     my ($self, $item, $element_state) = @_;
1718     $self->_remove_minus_elements ($element_state);
1719     unless ($self->{flag}->{has_hn}) {
1720     $self->{onerror}->(node => $item->{node},
1721     type => 'element missing:hn');
1722     }
1723     $self->{flag}->{has_hn} ||= $element_state->{has_hn_original};
1724 wakaba 1.1
1725 wakaba 1.40 $HTMLProseContentChecker{check_end}->(@_);
1726 wakaba 1.1 },
1727 wakaba 1.40 ## ISSUE: <header><del><h1>...</h1></del></header> is conforming?
1728 wakaba 1.1 };
1729    
1730     $Element->{$HTML_NS}->{footer} = {
1731 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
1732 wakaba 1.40 %HTMLProseContentChecker,
1733     check_start => sub {
1734     my ($self, $item, $element_state) = @_;
1735     $self->_add_minus_elements ($element_state,
1736     {$HTML_NS => {footer => 1}},
1737 wakaba 1.58 $HTMLSectioningContent,
1738 wakaba 1.57 $HTMLHeadingContent);
1739 wakaba 1.40 },
1740     check_end => sub {
1741     my ($self, $item, $element_state) = @_;
1742     $self->_remove_minus_elements ($element_state);
1743 wakaba 1.1
1744 wakaba 1.40 $HTMLProseContentChecker{check_end}->(@_);
1745 wakaba 1.1 },
1746     };
1747    
1748     $Element->{$HTML_NS}->{address} = {
1749 wakaba 1.40 %HTMLProseContentChecker,
1750 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1751     check_attrs => $GetHTMLAttrsChecker->({}, {
1752     %HTMLAttrStatus,
1753     %HTMLM12NCommonAttrStatus,
1754 wakaba 1.61 align => FEATURE_HTML2X_RFC,
1755 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1756 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
1757     sdapref => FEATURE_HTML20_RFC,
1758 wakaba 1.49 }),
1759 wakaba 1.40 check_start => sub {
1760     my ($self, $item, $element_state) = @_;
1761     $self->_add_minus_elements ($element_state,
1762     {$HTML_NS => {footer => 1, address => 1}},
1763     $HTMLSectioningContent, $HTMLHeadingContent);
1764     },
1765     check_end => sub {
1766     my ($self, $item, $element_state) = @_;
1767     $self->_remove_minus_elements ($element_state);
1768 wakaba 1.29
1769 wakaba 1.40 $HTMLProseContentChecker{check_end}->(@_);
1770 wakaba 1.29 },
1771 wakaba 1.1 };
1772    
1773     $Element->{$HTML_NS}->{p} = {
1774 wakaba 1.40 %HTMLPhrasingContentChecker,
1775 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1776 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
1777     align => $GetHTMLEnumeratedAttrChecker->({
1778     left => 1, center => 1, right => 1, justify => 1,
1779     }),
1780     }, {
1781 wakaba 1.49 %HTMLAttrStatus,
1782     %HTMLM12NCommonAttrStatus,
1783     align => FEATURE_M12N10_REC_DEPRECATED,
1784 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1785 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
1786 wakaba 1.49 }),
1787 wakaba 1.1 };
1788    
1789     $Element->{$HTML_NS}->{hr} = {
1790 wakaba 1.40 %HTMLEmptyChecker,
1791 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1792 wakaba 1.70 check_attrs => $GetHTMLAttrsChecker->({
1793     ## TODO: HTML4 |align|, |noshade|, |size|, |width|
1794     }, {
1795 wakaba 1.49 %HTMLAttrStatus,
1796     %HTMLM12NCommonAttrStatus,
1797     align => FEATURE_M12N10_REC_DEPRECATED,
1798 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1799 wakaba 1.49 noshade => FEATURE_M12N10_REC_DEPRECATED,
1800 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
1801 wakaba 1.49 size => FEATURE_M12N10_REC_DEPRECATED,
1802     width => FEATURE_M12N10_REC_DEPRECATED,
1803     }),
1804 wakaba 1.1 };
1805    
1806     $Element->{$HTML_NS}->{br} = {
1807 wakaba 1.40 %HTMLEmptyChecker,
1808 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1809 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
1810     clear => $GetHTMLEnumeratedAttrChecker->({
1811     left => 1, all => 1, right => 1, none => 1,
1812     }),
1813     }, {
1814 wakaba 1.49 %HTMLAttrStatus,
1815 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1816 wakaba 1.49 clear => FEATURE_M12N10_REC_DEPRECATED,
1817 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1818 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
1819 wakaba 1.49 style => FEATURE_XHTML10_REC,
1820 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1821 wakaba 1.49 }),
1822 wakaba 1.29 ## NOTE: Blank line MUST NOT be used for presentation purpose.
1823     ## (This requirement is semantic so that we cannot check.)
1824 wakaba 1.1 };
1825    
1826     $Element->{$HTML_NS}->{dialog} = {
1827 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
1828 wakaba 1.40 %HTMLChecker,
1829     check_start => sub {
1830     my ($self, $item, $element_state) = @_;
1831     $element_state->{phase} = 'before dt';
1832     },
1833     check_child_element => sub {
1834     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1835     $child_is_transparent, $element_state) = @_;
1836     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
1837     $self->{onerror}->(node => $child_el,
1838     type => 'element not allowed:minus',
1839     level => $self->{must_level});
1840     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1841     #
1842     } elsif ($element_state->{phase} eq 'before dt') {
1843     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
1844     $element_state->{phase} = 'before dd';
1845     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
1846     $self->{onerror}
1847     ->(node => $child_el, type => 'ps element missing:dt');
1848     $element_state->{phase} = 'before dt';
1849     } else {
1850     $self->{onerror}->(node => $child_el, type => 'element not allowed');
1851     }
1852     } elsif ($element_state->{phase} eq 'before dd') {
1853     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
1854     $element_state->{phase} = 'before dt';
1855     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
1856     $self->{onerror}
1857     ->(node => $child_el, type => 'ps element missing:dd');
1858     $element_state->{phase} = 'before dd';
1859     } else {
1860     $self->{onerror}->(node => $child_el, type => 'element not allowed');
1861 wakaba 1.1 }
1862 wakaba 1.40 } else {
1863     die "check_child_element: Bad |dialog| phase: $element_state->{phase}";
1864     }
1865     },
1866     check_child_text => sub {
1867     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1868     if ($has_significant) {
1869     $self->{onerror}->(node => $child_node, type => 'character not allowed');
1870 wakaba 1.1 }
1871 wakaba 1.40 },
1872     check_end => sub {
1873     my ($self, $item, $element_state) = @_;
1874     if ($element_state->{phase} eq 'before dd') {
1875     $self->{onerror}->(node => $item->{node},
1876     type => 'child element missing:dd');
1877 wakaba 1.1 }
1878 wakaba 1.40
1879     $HTMLChecker{check_end}->(@_);
1880 wakaba 1.1 },
1881     };
1882    
1883     $Element->{$HTML_NS}->{pre} = {
1884 wakaba 1.40 %HTMLPhrasingContentChecker,
1885 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1886 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
1887     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
1888     }, {
1889 wakaba 1.49 %HTMLAttrStatus,
1890     %HTMLM12NCommonAttrStatus,
1891 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1892 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
1893 wakaba 1.49 width => FEATURE_M12N10_REC_DEPRECATED,
1894     }),
1895 wakaba 1.1 };
1896    
1897     $Element->{$HTML_NS}->{ol} = {
1898 wakaba 1.40 %HTMLChecker,
1899 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1900 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
1901 wakaba 1.68 compact => $GetHTMLBooleanAttrChecker->('compact'),
1902 wakaba 1.69 reversed => $GetHTMLBooleanAttrChecker->('reversed'),
1903 wakaba 1.1 start => $HTMLIntegerAttrChecker,
1904 wakaba 1.69 ## TODO: HTML4 |type|
1905 wakaba 1.49 }, {
1906     %HTMLAttrStatus,
1907     %HTMLM12NCommonAttrStatus,
1908 wakaba 1.61 align => FEATURE_HTML2X_RFC,
1909 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
1910 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1911 wakaba 1.53 reversed => FEATURE_HTML5_DEFAULT,
1912 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
1913 wakaba 1.54 #start => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
1914     start => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1915 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
1916 wakaba 1.1 }),
1917 wakaba 1.40 check_child_element => sub {
1918     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1919     $child_is_transparent, $element_state) = @_;
1920     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
1921     $self->{onerror}->(node => $child_el,
1922     type => 'element not allowed:minus',
1923     level => $self->{must_level});
1924     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1925     #
1926     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'li') {
1927     #
1928     } else {
1929     $self->{onerror}->(node => $child_el, type => 'element not allowed');
1930 wakaba 1.1 }
1931 wakaba 1.40 },
1932     check_child_text => sub {
1933     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1934     if ($has_significant) {
1935     $self->{onerror}->(node => $child_node, type => 'character not allowed');
1936 wakaba 1.1 }
1937     },
1938     };
1939    
1940     $Element->{$HTML_NS}->{ul} = {
1941 wakaba 1.40 %{$Element->{$HTML_NS}->{ol}},
1942 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1943 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
1944     compact => $GetHTMLBooleanAttrChecker->('compact'),
1945 wakaba 1.69 ## TODO: HTML4 |type|
1946     ## TODO: sdaform, align
1947 wakaba 1.68 }, {
1948 wakaba 1.49 %HTMLAttrStatus,
1949     %HTMLM12NCommonAttrStatus,
1950 wakaba 1.61 align => FEATURE_HTML2X_RFC,
1951 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
1952 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1953 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
1954 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
1955     }),
1956 wakaba 1.1 };
1957    
1958 wakaba 1.64 $Element->{$HTML_NS}->{dir} = {
1959     ## TODO: %block; is not allowed [HTML4] ## TODO: Empty list allowed?
1960     %{$Element->{$HTML_NS}->{ul}},
1961     status => FEATURE_M12N10_REC_DEPRECATED,
1962 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
1963     compact => $GetHTMLBooleanAttrChecker->('compact'),
1964     }, {
1965 wakaba 1.64 %HTMLAttrStatus,
1966     %HTMLM12NCommonAttrStatus,
1967     align => FEATURE_HTML2X_RFC,
1968     compact => FEATURE_M12N10_REC_DEPRECATED,
1969     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1970     sdaform => FEATURE_HTML20_RFC,
1971     sdapref => FEATURE_HTML20_RFC,
1972     }),
1973     };
1974    
1975 wakaba 1.1 $Element->{$HTML_NS}->{li} = {
1976 wakaba 1.40 %HTMLProseContentChecker,
1977 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1978 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
1979 wakaba 1.69 ## TODO: HTML4 |type|
1980 wakaba 1.49 value => sub {
1981 wakaba 1.1 my ($self, $attr) = @_;
1982     my $parent = $attr->owner_element->manakai_parent_element;
1983     if (defined $parent) {
1984     my $parent_ns = $parent->namespace_uri;
1985     $parent_ns = '' unless defined $parent_ns;
1986     my $parent_ln = $parent->manakai_local_name;
1987     unless ($parent_ns eq $HTML_NS and $parent_ln eq 'ol') {
1988     $self->{onerror}->(node => $attr, level => 'unsupported',
1989     type => 'attribute');
1990     }
1991     }
1992     $HTMLIntegerAttrChecker->($self, $attr);
1993 wakaba 1.49 }, ## TODO: test
1994     }, {
1995     %HTMLAttrStatus,
1996     %HTMLM12NCommonAttrStatus,
1997 wakaba 1.61 align => FEATURE_HTML2X_RFC,
1998 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1999 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2000 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2001 wakaba 1.55 #value => FEATURE_HTML5_DEFAULT | FEATURE_XHTMLBASIC11_CR |
2002     # FEATURE_M12N10_REC_DEPRECATED,
2003     value => FEATURE_HTML5_DEFAULT | FEATURE_XHTMLBASIC11_CR |
2004     FEATURE_M12N10_REC,
2005 wakaba 1.1 }),
2006 wakaba 1.40 check_child_element => sub {
2007     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2008     $child_is_transparent, $element_state) = @_;
2009     if ($self->{flag}->{in_menu}) {
2010     $HTMLPhrasingContentChecker{check_child_element}->(@_);
2011     } else {
2012     $HTMLProseContentChecker{check_child_element}->(@_);
2013     }
2014     },
2015     check_child_text => sub {
2016     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2017     if ($self->{flag}->{in_menu}) {
2018     $HTMLPhrasingContentChecker{check_child_text}->(@_);
2019 wakaba 1.1 } else {
2020 wakaba 1.40 $HTMLProseContentChecker{check_child_text}->(@_);
2021 wakaba 1.1 }
2022     },
2023     };
2024    
2025     $Element->{$HTML_NS}->{dl} = {
2026 wakaba 1.40 %HTMLChecker,
2027 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2028 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2029     compact => $GetHTMLBooleanAttrChecker->('compact'),
2030     }, {
2031 wakaba 1.49 %HTMLAttrStatus,
2032     %HTMLM12NCommonAttrStatus,
2033     compact => FEATURE_M12N10_REC_DEPRECATED,
2034 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2035 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2036     sdapref => FEATURE_HTML20_RFC,
2037 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2038     }),
2039 wakaba 1.40 check_start => sub {
2040     my ($self, $item, $element_state) = @_;
2041     $element_state->{phase} = 'before dt';
2042     },
2043     check_child_element => sub {
2044     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2045     $child_is_transparent, $element_state) = @_;
2046     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
2047     $self->{onerror}->(node => $child_el,
2048     type => 'element not allowed:minus',
2049     level => $self->{must_level});
2050     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2051     #
2052     } elsif ($element_state->{phase} eq 'in dds') {
2053     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2054     #$element_state->{phase} = 'in dds';
2055     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2056     $element_state->{phase} = 'in dts';
2057     } else {
2058     $self->{onerror}->(node => $child_el, type => 'element not allowed');
2059     }
2060     } elsif ($element_state->{phase} eq 'in dts') {
2061     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2062     #$element_state->{phase} = 'in dts';
2063     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2064     $element_state->{phase} = 'in dds';
2065     } else {
2066     $self->{onerror}->(node => $child_el, type => 'element not allowed');
2067     }
2068     } elsif ($element_state->{phase} eq 'before dt') {
2069     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2070     $element_state->{phase} = 'in dts';
2071     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2072     $self->{onerror}
2073     ->(node => $child_el, type => 'ps element missing:dt');
2074     $element_state->{phase} = 'in dds';
2075     } else {
2076     $self->{onerror}->(node => $child_el, type => 'element not allowed');
2077 wakaba 1.1 }
2078 wakaba 1.40 } else {
2079     die "check_child_element: Bad |dl| phase: $element_state->{phase}";
2080 wakaba 1.1 }
2081 wakaba 1.40 },
2082     check_child_text => sub {
2083     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2084     if ($has_significant) {
2085     $self->{onerror}->(node => $child_node, type => 'character not allowed');
2086     }
2087     },
2088     check_end => sub {
2089     my ($self, $item, $element_state) = @_;
2090     if ($element_state->{phase} eq 'in dts') {
2091     $self->{onerror}->(node => $item->{node},
2092     type => 'child element missing:dd');
2093 wakaba 1.1 }
2094    
2095 wakaba 1.40 $HTMLChecker{check_end}->(@_);
2096 wakaba 1.1 },
2097     };
2098    
2099     $Element->{$HTML_NS}->{dt} = {
2100 wakaba 1.40 %HTMLPhrasingContentChecker,
2101 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2102     check_attrs => $GetHTMLAttrsChecker->({}, {
2103     %HTMLAttrStatus,
2104     %HTMLM12NCommonAttrStatus,
2105 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2106 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2107 wakaba 1.49 }),
2108 wakaba 1.1 };
2109    
2110     $Element->{$HTML_NS}->{dd} = {
2111 wakaba 1.40 %HTMLProseContentChecker,
2112 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2113     check_attrs => $GetHTMLAttrsChecker->({}, {
2114     %HTMLAttrStatus,
2115     %HTMLM12NCommonAttrStatus,
2116 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2117 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2118 wakaba 1.49 }),
2119 wakaba 1.1 };
2120    
2121     $Element->{$HTML_NS}->{a} = {
2122 wakaba 1.40 %HTMLPhrasingContentChecker,
2123 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2124 wakaba 1.40 check_attrs => sub {
2125     my ($self, $item, $element_state) = @_;
2126 wakaba 1.1 my %attr;
2127 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
2128 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
2129     $attr_ns = '' unless defined $attr_ns;
2130     my $attr_ln = $attr->manakai_local_name;
2131     my $checker;
2132     if ($attr_ns eq '') {
2133     $checker = {
2134 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
2135 wakaba 1.70 ## TODO: HTML4 |charset|
2136     ## TODO: HTML4 |coords|
2137 wakaba 1.1 target => $HTMLTargetAttrChecker,
2138     href => $HTMLURIAttrChecker,
2139     ping => $HTMLSpaceURIsAttrChecker,
2140 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) },
2141 wakaba 1.71 rev => $HTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker,
2142 wakaba 1.70 ## TODO: HTML4 |shape|
2143 wakaba 1.1 media => $HTMLMQAttrChecker,
2144 wakaba 1.70 ## TODO: HTML4/XHTML1 |name|
2145 wakaba 1.1 hreflang => $HTMLLanguageTagAttrChecker,
2146     type => $HTMLIMTAttrChecker,
2147     }->{$attr_ln};
2148     if ($checker) {
2149     $attr{$attr_ln} = $attr;
2150     } else {
2151     $checker = $HTMLAttrChecker->{$attr_ln};
2152     }
2153     }
2154     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
2155     || $AttrChecker->{$attr_ns}->{''};
2156 wakaba 1.62
2157     my $status = {
2158     %HTMLAttrStatus,
2159     %HTMLM12NCommonAttrStatus,
2160     accesskey => FEATURE_M12N10_REC,
2161     charset => FEATURE_M12N10_REC,
2162     coords => FEATURE_M12N10_REC,
2163     cryptopts => FEATURE_RFC2659,
2164     dn => FEATURE_RFC2659,
2165     href => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2166     hreflang => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2167     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2168     media => FEATURE_HTML5_DEFAULT,
2169     methods => FEATURE_HTML20_RFC,
2170     name => FEATURE_M12N10_REC_DEPRECATED,
2171     nonce => FEATURE_RFC2659,
2172     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2173     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2174     ping => FEATURE_HTML5_DEFAULT,
2175     rel => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2176     rev => FEATURE_M12N10_REC,
2177     sdapref => FEATURE_HTML20_RFC,
2178     shape => FEATURE_M12N10_REC,
2179     tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2180     target => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2181     type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2182     urn => FEATURE_HTML20_RFC,
2183     }->{$attr_ln};
2184    
2185 wakaba 1.1 if ($checker) {
2186 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
2187 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
2188 wakaba 1.54 #
2189 wakaba 1.1 } else {
2190     $self->{onerror}->(node => $attr, level => 'unsupported',
2191     type => 'attribute');
2192 wakaba 1.50 ## ISSUE: No conformance createria for unknown attributes in the spec
2193 wakaba 1.1 }
2194 wakaba 1.49
2195     if ($attr_ns eq '') {
2196 wakaba 1.62 $self->_attr_status_info ($attr, $status);
2197 wakaba 1.49 }
2198 wakaba 1.1 }
2199    
2200 wakaba 1.40 $element_state->{in_a_href_original} = $self->{flag}->{in_a_href};
2201 wakaba 1.4 if (defined $attr{href}) {
2202     $self->{has_hyperlink_element} = 1;
2203 wakaba 1.40 $self->{flag}->{in_a_href} = 1;
2204 wakaba 1.4 } else {
2205 wakaba 1.1 for (qw/target ping rel media hreflang type/) {
2206     if (defined $attr{$_}) {
2207     $self->{onerror}->(node => $attr{$_},
2208     type => 'attribute not allowed');
2209     }
2210     }
2211     }
2212 wakaba 1.66
2213     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
2214 wakaba 1.1 },
2215 wakaba 1.40 check_start => sub {
2216     my ($self, $item, $element_state) = @_;
2217     $self->_add_minus_elements ($element_state, $HTMLInteractiveContent);
2218     },
2219     check_end => sub {
2220     my ($self, $item, $element_state) = @_;
2221     $self->_remove_minus_elements ($element_state);
2222 wakaba 1.59 delete $self->{flag}->{in_a_href}
2223     unless $element_state->{in_a_href_original};
2224 wakaba 1.1
2225 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
2226 wakaba 1.1 },
2227     };
2228    
2229     $Element->{$HTML_NS}->{q} = {
2230 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2231 wakaba 1.40 %HTMLPhrasingContentChecker,
2232     check_attrs => $GetHTMLAttrsChecker->({
2233 wakaba 1.50 cite => $HTMLURIAttrChecker,
2234     }, {
2235 wakaba 1.49 %HTMLAttrStatus,
2236     %HTMLM12NCommonAttrStatus,
2237 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2238     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2239 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
2240     sdasuff => FEATURE_HTML2X_RFC,
2241 wakaba 1.1 }),
2242 wakaba 1.66 check_start => sub {
2243     my ($self, $item, $element_state) = @_;
2244    
2245     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
2246     },
2247 wakaba 1.1 };
2248    
2249     $Element->{$HTML_NS}->{cite} = {
2250 wakaba 1.40 %HTMLPhrasingContentChecker,
2251 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2252     check_attrs => $GetHTMLAttrsChecker->({}, {
2253     %HTMLAttrStatus,
2254     %HTMLM12NCommonAttrStatus,
2255 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2256 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2257 wakaba 1.49 }),
2258 wakaba 1.1 };
2259    
2260     $Element->{$HTML_NS}->{em} = {
2261 wakaba 1.40 %HTMLPhrasingContentChecker,
2262 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2263     check_attrs => $GetHTMLAttrsChecker->({}, {
2264     %HTMLAttrStatus,
2265     %HTMLM12NCommonAttrStatus,
2266 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2267 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2268 wakaba 1.49 }),
2269 wakaba 1.1 };
2270    
2271     $Element->{$HTML_NS}->{strong} = {
2272 wakaba 1.40 %HTMLPhrasingContentChecker,
2273 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2274     check_attrs => $GetHTMLAttrsChecker->({}, {
2275     %HTMLAttrStatus,
2276     %HTMLM12NCommonAttrStatus,
2277 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2278 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2279 wakaba 1.49 }),
2280 wakaba 1.1 };
2281    
2282     $Element->{$HTML_NS}->{small} = {
2283 wakaba 1.40 %HTMLPhrasingContentChecker,
2284 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2285     check_attrs => $GetHTMLAttrsChecker->({}, {
2286     %HTMLAttrStatus,
2287     %HTMLM12NCommonAttrStatus,
2288 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2289 wakaba 1.49 }),
2290 wakaba 1.1 };
2291    
2292 wakaba 1.51 $Element->{$HTML_NS}->{big} = {
2293     %HTMLPhrasingContentChecker,
2294     status => FEATURE_M12N10_REC,
2295     check_attrs => $GetHTMLAttrsChecker->({}, {
2296     %HTMLAttrStatus,
2297     %HTMLM12NCommonAttrStatus,
2298     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2299     }),
2300     };
2301    
2302 wakaba 1.38 $Element->{$HTML_NS}->{mark} = {
2303 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2304 wakaba 1.40 %HTMLPhrasingContentChecker,
2305 wakaba 1.1 };
2306    
2307     $Element->{$HTML_NS}->{dfn} = {
2308 wakaba 1.40 %HTMLPhrasingContentChecker,
2309 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2310     check_attrs => $GetHTMLAttrsChecker->({}, {
2311     %HTMLAttrStatus,
2312     %HTMLM12NCommonAttrStatus,
2313 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2314 wakaba 1.49 }),
2315 wakaba 1.40 check_start => sub {
2316     my ($self, $item, $element_state) = @_;
2317     $self->_add_minus_elements ($element_state, {$HTML_NS => {dfn => 1}});
2318 wakaba 1.1
2319 wakaba 1.40 my $node = $item->{node};
2320 wakaba 1.1 my $term = $node->get_attribute_ns (undef, 'title');
2321     unless (defined $term) {
2322     for my $child (@{$node->child_nodes}) {
2323     if ($child->node_type == 1) { # ELEMENT_NODE
2324     if (defined $term) {
2325     undef $term;
2326     last;
2327     } elsif ($child->manakai_local_name eq 'abbr') {
2328     my $nsuri = $child->namespace_uri;
2329     if (defined $nsuri and $nsuri eq $HTML_NS) {
2330     my $attr = $child->get_attribute_node_ns (undef, 'title');
2331     if ($attr) {
2332     $term = $attr->value;
2333     }
2334     }
2335     }
2336     } elsif ($child->node_type == 3 or $child->node_type == 4) {
2337     ## TEXT_NODE or CDATA_SECTION_NODE
2338     if ($child->data =~ /\A[\x09-\x0D\x20]+\z/) { # Inter-element whitespace
2339     next;
2340     }
2341     undef $term;
2342     last;
2343     }
2344     }
2345     unless (defined $term) {
2346     $term = $node->text_content;
2347     }
2348     }
2349     if ($self->{term}->{$term}) {
2350     $self->{onerror}->(node => $node, type => 'duplicate term');
2351     push @{$self->{term}->{$term}}, $node;
2352     } else {
2353     $self->{term}->{$term} = [$node];
2354     }
2355     ## ISSUE: The HTML5 algorithm does not work with |ruby| unless |dfn|
2356     ## has |title|.
2357 wakaba 1.40 },
2358     check_end => sub {
2359     my ($self, $item, $element_state) = @_;
2360     $self->_remove_minus_elements ($element_state);
2361 wakaba 1.1
2362 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
2363 wakaba 1.1 },
2364     };
2365    
2366     $Element->{$HTML_NS}->{abbr} = {
2367 wakaba 1.40 %HTMLPhrasingContentChecker,
2368 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2369     check_attrs => $GetHTMLAttrsChecker->({}, {
2370     %HTMLAttrStatus,
2371     %HTMLM12NCommonAttrStatus,
2372 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2373 wakaba 1.49 }),
2374     };
2375    
2376     $Element->{$HTML_NS}->{acronym} = {
2377     %HTMLPhrasingContentChecker,
2378     status => FEATURE_M12N10_REC,
2379     check_attrs => $GetHTMLAttrsChecker->({}, {
2380     %HTMLAttrStatus,
2381     %HTMLM12NCommonAttrStatus,
2382 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2383 wakaba 1.49 }),
2384 wakaba 1.1 };
2385    
2386     $Element->{$HTML_NS}->{time} = {
2387 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2388 wakaba 1.40 %HTMLPhrasingContentChecker,
2389     check_attrs => $GetHTMLAttrsChecker->({
2390 wakaba 1.1 datetime => sub { 1 }, # checked in |checker|
2391 wakaba 1.49 }, {
2392     %HTMLAttrStatus,
2393     %HTMLM12NCommonAttrStatus,
2394 wakaba 1.50 datetime => FEATURE_HTML5_DEFAULT,
2395 wakaba 1.1 }),
2396     ## TODO: Write tests
2397 wakaba 1.40 check_end => sub {
2398     my ($self, $item, $element_state) = @_;
2399 wakaba 1.1
2400 wakaba 1.40 my $attr = $item->{node}->get_attribute_node_ns (undef, 'datetime');
2401 wakaba 1.1 my $input;
2402     my $reg_sp;
2403     my $input_node;
2404     if ($attr) {
2405     $input = $attr->value;
2406     $reg_sp = qr/[\x09-\x0D\x20]*/;
2407     $input_node = $attr;
2408     } else {
2409 wakaba 1.40 $input = $item->{node}->text_content;
2410 wakaba 1.1 $reg_sp = qr/\p{Zs}*/;
2411 wakaba 1.40 $input_node = $item->{node};
2412 wakaba 1.1
2413     ## ISSUE: What is the definition for "successfully extracts a date
2414     ## or time"? If the algorithm says the string is invalid but
2415     ## return some date or time, is it "successfully"?
2416     }
2417    
2418     my $hour;
2419     my $minute;
2420     my $second;
2421     if ($input =~ /
2422     \A
2423     [\x09-\x0D\x20]*
2424     ([0-9]+) # 1
2425     (?>
2426     -([0-9]+) # 2
2427     -([0-9]+) # 3
2428     [\x09-\x0D\x20]*
2429     (?>
2430     T
2431     [\x09-\x0D\x20]*
2432     )?
2433     ([0-9]+) # 4
2434     :([0-9]+) # 5
2435     (?>
2436     :([0-9]+(?>\.[0-9]*)?|\.[0-9]*) # 6
2437     )?
2438     [\x09-\x0D\x20]*
2439     (?>
2440     Z
2441     [\x09-\x0D\x20]*
2442     |
2443     [+-]([0-9]+):([0-9]+) # 7, 8
2444     [\x09-\x0D\x20]*
2445     )?
2446     \z
2447     |
2448     :([0-9]+) # 9
2449     (?>
2450     :([0-9]+(?>\.[0-9]*)?|\.[0-9]*) # 10
2451     )?
2452     [\x09-\x0D\x20]*\z
2453     )
2454     /x) {
2455     if (defined $2) { ## YYYY-MM-DD T? hh:mm
2456     if (length $1 != 4 or length $2 != 2 or length $3 != 2 or
2457     length $4 != 2 or length $5 != 2) {
2458     $self->{onerror}->(node => $input_node,
2459     type => 'dateortime:syntax error');
2460     }
2461    
2462     if (1 <= $2 and $2 <= 12) {
2463     $self->{onerror}->(node => $input_node, type => 'datetime:bad day')
2464     if $3 < 1 or
2465     $3 > [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]->[$2];
2466     $self->{onerror}->(node => $input_node, type => 'datetime:bad day')
2467     if $2 == 2 and $3 == 29 and
2468     not ($1 % 400 == 0 or ($1 % 4 == 0 and $1 % 100 != 0));
2469     } else {
2470     $self->{onerror}->(node => $input_node,
2471     type => 'datetime:bad month');
2472     }
2473    
2474     ($hour, $minute, $second) = ($4, $5, $6);
2475    
2476     if (defined $7) { ## [+-]hh:mm
2477     if (length $7 != 2 or length $8 != 2) {
2478     $self->{onerror}->(node => $input_node,
2479     type => 'dateortime:syntax error');
2480     }
2481    
2482     $self->{onerror}->(node => $input_node,
2483     type => 'datetime:bad timezone hour')
2484     if $7 > 23;
2485     $self->{onerror}->(node => $input_node,
2486     type => 'datetime:bad timezone minute')
2487     if $8 > 59;
2488     }
2489     } else { ## hh:mm
2490     if (length $1 != 2 or length $9 != 2) {
2491     $self->{onerror}->(node => $input_node,
2492     type => qq'dateortime:syntax error');
2493     }
2494    
2495     ($hour, $minute, $second) = ($1, $9, $10);
2496     }
2497    
2498     $self->{onerror}->(node => $input_node, type => 'datetime:bad hour')
2499     if $hour > 23;
2500     $self->{onerror}->(node => $input_node, type => 'datetime:bad minute')
2501     if $minute > 59;
2502    
2503     if (defined $second) { ## s
2504     ## NOTE: Integer part of second don't have to have length of two.
2505    
2506     if (substr ($second, 0, 1) eq '.') {
2507     $self->{onerror}->(node => $input_node,
2508     type => 'dateortime:syntax error');
2509     }
2510    
2511     $self->{onerror}->(node => $input_node, type => 'datetime:bad second')
2512     if $second >= 60;
2513     }
2514     } else {
2515     $self->{onerror}->(node => $input_node,
2516     type => 'dateortime:syntax error');
2517     }
2518    
2519 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
2520 wakaba 1.1 },
2521     };
2522    
2523     $Element->{$HTML_NS}->{meter} = { ## TODO: "The recommended way of giving the value is to include it as contents of the element"
2524 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2525 wakaba 1.40 %HTMLPhrasingContentChecker,
2526     check_attrs => $GetHTMLAttrsChecker->({
2527 wakaba 1.1 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
2528     min => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
2529     low => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
2530     high => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
2531     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
2532     optimum => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
2533 wakaba 1.50 }, {
2534     %HTMLAttrStatus,
2535     high => FEATURE_HTML5_DEFAULT,
2536     low => FEATURE_HTML5_DEFAULT,
2537     max => FEATURE_HTML5_DEFAULT,
2538     min => FEATURE_HTML5_DEFAULT,
2539     optimum => FEATURE_HTML5_DEFAULT,
2540     value => FEATURE_HTML5_DEFAULT,
2541 wakaba 1.1 }),
2542     };
2543    
2544     $Element->{$HTML_NS}->{progress} = { ## TODO: recommended to use content
2545 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2546 wakaba 1.40 %HTMLPhrasingContentChecker,
2547     check_attrs => $GetHTMLAttrsChecker->({
2548 wakaba 1.1 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift >= 0 }),
2549     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift > 0 }),
2550 wakaba 1.50 }, {
2551     %HTMLAttrStatus,
2552     max => FEATURE_HTML5_DEFAULT,
2553     value => FEATURE_HTML5_DEFAULT,
2554 wakaba 1.1 }),
2555     };
2556    
2557     $Element->{$HTML_NS}->{code} = {
2558 wakaba 1.40 %HTMLPhrasingContentChecker,
2559 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2560     check_attrs => $GetHTMLAttrsChecker->({}, {
2561     %HTMLAttrStatus,
2562     %HTMLM12NCommonAttrStatus,
2563 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2564 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2565 wakaba 1.49 }),
2566 wakaba 1.1 };
2567    
2568     $Element->{$HTML_NS}->{var} = {
2569 wakaba 1.40 %HTMLPhrasingContentChecker,
2570 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2571     check_attrs => $GetHTMLAttrsChecker->({}, {
2572     %HTMLAttrStatus,
2573     %HTMLM12NCommonAttrStatus,
2574 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2575 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2576 wakaba 1.49 }),
2577 wakaba 1.1 };
2578    
2579     $Element->{$HTML_NS}->{samp} = {
2580 wakaba 1.40 %HTMLPhrasingContentChecker,
2581 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2582     check_attrs => $GetHTMLAttrsChecker->({}, {
2583     %HTMLAttrStatus,
2584     %HTMLM12NCommonAttrStatus,
2585 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2586 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2587 wakaba 1.49 }),
2588 wakaba 1.1 };
2589    
2590     $Element->{$HTML_NS}->{kbd} = {
2591 wakaba 1.40 %HTMLPhrasingContentChecker,
2592 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2593     check_attrs => $GetHTMLAttrsChecker->({}, {
2594     %HTMLAttrStatus,
2595     %HTMLM12NCommonAttrStatus,
2596 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2597 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2598 wakaba 1.49 }),
2599 wakaba 1.1 };
2600    
2601     $Element->{$HTML_NS}->{sub} = {
2602 wakaba 1.40 %HTMLPhrasingContentChecker,
2603 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2604     check_attrs => $GetHTMLAttrsChecker->({}, {
2605     %HTMLAttrStatus,
2606     %HTMLM12NCommonAttrStatus,
2607 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2608 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
2609 wakaba 1.49 }),
2610 wakaba 1.1 };
2611    
2612 wakaba 1.51 $Element->{$HTML_NS}->{sup} = $Element->{$HTML_NS}->{sub};
2613 wakaba 1.1
2614     $Element->{$HTML_NS}->{span} = {
2615 wakaba 1.40 %HTMLPhrasingContentChecker,
2616 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2617     check_attrs => $GetHTMLAttrsChecker->({}, {
2618     %HTMLAttrStatus,
2619     %HTMLM12NCommonAttrStatus,
2620     datafld => FEATURE_HTML4_REC_RESERVED,
2621     dataformatas => FEATURE_HTML4_REC_RESERVED,
2622     datasrc => FEATURE_HTML4_REC_RESERVED,
2623 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2624 wakaba 1.61 sdaform => FEATURE_HTML2X_RFC,
2625 wakaba 1.49 }),
2626 wakaba 1.1 };
2627    
2628     $Element->{$HTML_NS}->{i} = {
2629 wakaba 1.40 %HTMLPhrasingContentChecker,
2630 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2631     check_attrs => $GetHTMLAttrsChecker->({}, {
2632     %HTMLAttrStatus,
2633     %HTMLM12NCommonAttrStatus,
2634 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2635 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2636 wakaba 1.49 }),
2637 wakaba 1.1 };
2638    
2639 wakaba 1.51 $Element->{$HTML_NS}->{b} = $Element->{$HTML_NS}->{i};
2640    
2641 wakaba 1.61 $Element->{$HTML_NS}->{tt} = {
2642     %HTMLPhrasingContentChecker,
2643     status => FEATURE_M12N10_REC,
2644     check_attrs => $GetHTMLAttrsChecker->({}, {
2645     %HTMLAttrStatus,
2646     %HTMLM12NCommonAttrStatus,
2647     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2648     sdaform => FEATURE_HTML20_RFC,
2649     }),
2650     };
2651 wakaba 1.51
2652     $Element->{$HTML_NS}->{s} = {
2653 wakaba 1.40 %HTMLPhrasingContentChecker,
2654 wakaba 1.51 status => FEATURE_M12N10_REC_DEPRECATED,
2655 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2656     %HTMLAttrStatus,
2657     %HTMLM12NCommonAttrStatus,
2658 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2659 wakaba 1.49 }),
2660 wakaba 1.1 };
2661    
2662 wakaba 1.51 $Element->{$HTML_NS}->{strike} = $Element->{$HTML_NS}->{s};
2663    
2664     $Element->{$HTML_NS}->{u} = $Element->{$HTML_NS}->{s};
2665    
2666 wakaba 1.1 $Element->{$HTML_NS}->{bdo} = {
2667 wakaba 1.40 %HTMLPhrasingContentChecker,
2668 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2669 wakaba 1.40 check_attrs => sub {
2670     my ($self, $item, $element_state) = @_;
2671 wakaba 1.49 $GetHTMLAttrsChecker->({}, {
2672     %HTMLAttrStatus,
2673 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2674     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2675     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2676 wakaba 1.49 style => FEATURE_XHTML10_REC,
2677 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2678     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2679 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
2680     sdasuff => FEATURE_HTML2X_RFC,
2681 wakaba 1.49 })->($self, $item, $element_state);
2682 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'dir')) {
2683     $self->{onerror}->(node => $item->{node},
2684     type => 'attribute missing:dir');
2685 wakaba 1.1 }
2686     },
2687     ## ISSUE: The spec does not directly say that |dir| is a enumerated attr.
2688     };
2689    
2690 wakaba 1.29 =pod
2691    
2692     ## TODO:
2693    
2694     +
2695     + <p>Partly because of the confusion described above, authors are
2696     + strongly recommended to always mark up all paragraphs with the
2697     + <code>p</code> element, and to not have any <code>ins</code> or
2698     + <code>del</code> elements that cross across any <span
2699     + title="paragraph">implied paragraphs</span>.</p>
2700     +
2701     (An informative note)
2702    
2703     <p><code>ins</code> elements should not cross <span
2704     + title="paragraph">implied paragraph</span> boundaries.</p>
2705     (normative)
2706    
2707     + <p><code>del</code> elements should not cross <span
2708     + title="paragraph">implied paragraph</span> boundaries.</p>
2709     (normative)
2710    
2711     =cut
2712    
2713 wakaba 1.1 $Element->{$HTML_NS}->{ins} = {
2714 wakaba 1.40 %HTMLTransparentChecker,
2715 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2716 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2717 wakaba 1.1 cite => $HTMLURIAttrChecker,
2718     datetime => $HTMLDatetimeAttrChecker,
2719 wakaba 1.49 }, {
2720     %HTMLAttrStatus,
2721     %HTMLM12NCommonAttrStatus,
2722 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2723     datetime => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2724     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2725 wakaba 1.1 }),
2726 wakaba 1.66 check_start => sub {
2727     my ($self, $item, $element_state) = @_;
2728    
2729     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
2730     },
2731 wakaba 1.1 };
2732    
2733     $Element->{$HTML_NS}->{del} = {
2734 wakaba 1.40 %HTMLTransparentChecker,
2735 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2736 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2737 wakaba 1.1 cite => $HTMLURIAttrChecker,
2738     datetime => $HTMLDatetimeAttrChecker,
2739 wakaba 1.49 }, {
2740     %HTMLAttrStatus,
2741     %HTMLM12NCommonAttrStatus,
2742 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2743     datetime => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2744     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2745 wakaba 1.1 }),
2746 wakaba 1.40 check_end => sub {
2747     my ($self, $item, $element_state) = @_;
2748     if ($element_state->{has_significant}) {
2749     ## NOTE: Significantness flag does not propagate.
2750     } elsif ($item->{transparent}) {
2751     #
2752     } else {
2753     $self->{onerror}->(node => $item->{node},
2754     level => $self->{should_level},
2755     type => 'no significant content');
2756     }
2757 wakaba 1.1 },
2758 wakaba 1.66 check_start => sub {
2759     my ($self, $item, $element_state) = @_;
2760    
2761     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
2762     },
2763 wakaba 1.1 };
2764    
2765 wakaba 1.35 $Element->{$HTML_NS}->{figure} = {
2766 wakaba 1.40 %HTMLProseContentChecker,
2767 wakaba 1.48 status => FEATURE_HTML5_FD,
2768 wakaba 1.53 ## NOTE: legend, Prose | Prose, legend?
2769 wakaba 1.41 check_child_element => sub {
2770     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2771     $child_is_transparent, $element_state) = @_;
2772     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
2773     $self->{onerror}->(node => $child_el,
2774     type => 'element not allowed:minus',
2775     level => $self->{must_level});
2776     $element_state->{has_non_legend} = 1;
2777     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2778     #
2779     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') {
2780     if ($element_state->{has_legend_at_first}) {
2781     $self->{onerror}->(node => $child_el,
2782     type => 'element not allowed:figure legend',
2783     level => $self->{must_level});
2784     } elsif ($element_state->{has_legend}) {
2785     $self->{onerror}->(node => $element_state->{has_legend},
2786     type => 'element not allowed:figure legend',
2787     level => $self->{must_level});
2788     $element_state->{has_legend} = $child_el;
2789     } elsif ($element_state->{has_non_legend}) {
2790     $element_state->{has_legend} = $child_el;
2791     } else {
2792     $element_state->{has_legend_at_first} = 1;
2793 wakaba 1.35 }
2794 wakaba 1.41 delete $element_state->{has_non_legend};
2795     } else {
2796     $HTMLProseContentChecker{check_child_element}->(@_);
2797 wakaba 1.43 $element_state->{has_non_legend} = 1 unless $child_is_transparent;
2798 wakaba 1.41 }
2799     },
2800     check_child_text => sub {
2801     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2802     if ($has_significant) {
2803     $element_state->{has_non_legend} = 1;
2804 wakaba 1.35 }
2805 wakaba 1.41 },
2806     check_end => sub {
2807     my ($self, $item, $element_state) = @_;
2808 wakaba 1.35
2809 wakaba 1.41 if ($element_state->{has_legend_at_first}) {
2810     #
2811     } elsif ($element_state->{has_legend}) {
2812     if ($element_state->{has_non_legend}) {
2813     $self->{onerror}->(node => $element_state->{has_legend},
2814 wakaba 1.35 type => 'element not allowed:figure legend',
2815     level => $self->{must_level});
2816     }
2817     }
2818 wakaba 1.41
2819     $HTMLProseContentChecker{check_end}->(@_);
2820     ## ISSUE: |<figure><legend>aa</legend></figure>| should be an error?
2821 wakaba 1.35 },
2822     };
2823 wakaba 1.8 ## TODO: Test for <nest/> in <figure/>
2824 wakaba 1.1
2825     $Element->{$HTML_NS}->{img} = {
2826 wakaba 1.40 %HTMLEmptyChecker,
2827 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2828 wakaba 1.40 check_attrs => sub {
2829     my ($self, $item, $element_state) = @_;
2830 wakaba 1.1 $GetHTMLAttrsChecker->({
2831 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
2832     bottom => 1, middle => 1, top => 1, left => 1, right => 1,
2833     }),
2834 wakaba 1.1 alt => sub { }, ## NOTE: No syntactical requirement
2835 wakaba 1.70 border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
2836 wakaba 1.1 src => $HTMLURIAttrChecker,
2837     usemap => $HTMLUsemapAttrChecker,
2838 wakaba 1.70 hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
2839 wakaba 1.1 ismap => sub {
2840 wakaba 1.40 my ($self, $attr, $parent_item) = @_;
2841     if (not $self->{flag}->{in_a_href}) {
2842 wakaba 1.15 $self->{onerror}->(node => $attr,
2843 wakaba 1.59 type => 'attribute not allowed:ismap',
2844     level => $self->{must_level});
2845 wakaba 1.1 }
2846 wakaba 1.40 $GetHTMLBooleanAttrChecker->('ismap')->($self, $attr, $parent_item);
2847 wakaba 1.1 },
2848 wakaba 1.70 longdesc => $HTMLURIAttrChecker,
2849     ## TODO: HTML4 |name|
2850 wakaba 1.1 ## TODO: height
2851 wakaba 1.70 vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
2852 wakaba 1.1 ## TODO: width
2853 wakaba 1.49 }, {
2854     %HTMLAttrStatus,
2855     %HTMLM12NCommonAttrStatus,
2856     align => FEATURE_M12N10_REC_DEPRECATED,
2857 wakaba 1.50 alt => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2858 wakaba 1.49 border => FEATURE_M12N10_REC_DEPRECATED,
2859 wakaba 1.50 height => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2860 wakaba 1.49 hspace => FEATURE_M12N10_REC_DEPRECATED,
2861 wakaba 1.50 ismap => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2862     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2863 wakaba 1.49 longdesc => FEATURE_M12N10_REC,
2864     name => FEATURE_M12N10_REC_DEPRECATED,
2865 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
2866 wakaba 1.50 src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2867     usemap => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2868 wakaba 1.49 vspace => FEATURE_M12N10_REC_DEPRECATED,
2869 wakaba 1.50 width => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2870 wakaba 1.66 })->($self, $item, $element_state);
2871 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'alt')) {
2872     $self->{onerror}->(node => $item->{node},
2873 wakaba 1.37 type => 'attribute missing:alt',
2874     level => $self->{should_level});
2875 wakaba 1.1 }
2876 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'src')) {
2877     $self->{onerror}->(node => $item->{node},
2878     type => 'attribute missing:src');
2879 wakaba 1.1 }
2880 wakaba 1.66
2881     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
2882     $element_state->{uri_info}->{lowsrc}->{type}->{embedded} = 1;
2883     $element_state->{uri_info}->{dynsrc}->{type}->{embedded} = 1;
2884     $element_state->{uri_info}->{longdesc}->{type}->{cite} = 1;
2885 wakaba 1.1 },
2886     };
2887    
2888     $Element->{$HTML_NS}->{iframe} = {
2889 wakaba 1.40 %HTMLTextChecker,
2890 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2891     ## NOTE: Not part of M12N10 Strict
2892 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2893 wakaba 1.1 src => $HTMLURIAttrChecker,
2894 wakaba 1.49 }, {
2895     %HTMLAttrStatus,
2896     %HTMLM12NCommonAttrStatus,
2897     align => FEATURE_XHTML10_REC,
2898 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2899 wakaba 1.49 frameborder => FEATURE_M12N10_REC,
2900     height => FEATURE_M12N10_REC,
2901 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2902 wakaba 1.49 longdesc => FEATURE_M12N10_REC,
2903     marginheight => FEATURE_M12N10_REC,
2904     marginwidth => FEATURE_M12N10_REC,
2905     name => FEATURE_M12N10_REC_DEPRECATED,
2906     scrolling => FEATURE_M12N10_REC,
2907 wakaba 1.50 src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2908     title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2909 wakaba 1.49 width => FEATURE_M12N10_REC,
2910 wakaba 1.1 }),
2911 wakaba 1.66 check_start => sub {
2912     my ($self, $item, $element_state) = @_;
2913    
2914     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
2915     },
2916 wakaba 1.40 };
2917    
2918 wakaba 1.1 $Element->{$HTML_NS}->{embed} = {
2919 wakaba 1.40 %HTMLEmptyChecker,
2920 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2921 wakaba 1.40 check_attrs => sub {
2922     my ($self, $item, $element_state) = @_;
2923 wakaba 1.1 my $has_src;
2924 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
2925 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
2926     $attr_ns = '' unless defined $attr_ns;
2927     my $attr_ln = $attr->manakai_local_name;
2928     my $checker;
2929     if ($attr_ns eq '') {
2930     if ($attr_ln eq 'src') {
2931     $checker = $HTMLURIAttrChecker;
2932     $has_src = 1;
2933     } elsif ($attr_ln eq 'type') {
2934     $checker = $HTMLIMTAttrChecker;
2935     } else {
2936     ## TODO: height
2937     ## TODO: width
2938     $checker = $HTMLAttrChecker->{$attr_ln}
2939     || sub { }; ## NOTE: Any local attribute is ok.
2940     }
2941     }
2942     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
2943     || $AttrChecker->{$attr_ns}->{''};
2944 wakaba 1.62
2945     my $status = {
2946     %HTMLAttrStatus,
2947     height => FEATURE_HTML5_DEFAULT,
2948     src => FEATURE_HTML5_DEFAULT,
2949     type => FEATURE_HTML5_DEFAULT,
2950     width => FEATURE_HTML5_DEFAULT,
2951     }->{$attr_ln};
2952    
2953 wakaba 1.1 if ($checker) {
2954 wakaba 1.66 $checker->($self, $attr, $item, $element_state);
2955 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
2956 wakaba 1.54 #
2957 wakaba 1.1 } else {
2958     $self->{onerror}->(node => $attr, level => 'unsupported',
2959     type => 'attribute');
2960 wakaba 1.50 ## ISSUE: No conformance createria for global attributes in the spec
2961     }
2962    
2963     if ($attr_ns eq '') {
2964     $self->_attr_status_info ($attr, $status) if $status;
2965 wakaba 1.1 }
2966     }
2967    
2968     unless ($has_src) {
2969 wakaba 1.40 $self->{onerror}->(node => $item->{node},
2970 wakaba 1.1 type => 'attribute missing:src');
2971     }
2972 wakaba 1.66
2973     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
2974 wakaba 1.1 },
2975     };
2976    
2977 wakaba 1.49 ## TODO:
2978     ## {applet} FEATURE_M12N10_REC_DEPRECATED
2979     ## class, id, title, alt, archive, code, codebase, height, object, width name style,hspace,vspace(xhtml10)
2980    
2981 wakaba 1.1 $Element->{$HTML_NS}->{object} = {
2982 wakaba 1.40 %HTMLTransparentChecker,
2983 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2984 wakaba 1.40 check_attrs => sub {
2985     my ($self, $item, $element_state) = @_;
2986 wakaba 1.1 $GetHTMLAttrsChecker->({
2987 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
2988     bottom => 1, middle => 1, top => 1, left => 1, right => 1,
2989     }),
2990     archive => $HTMLSpaceURIsAttrChecker,
2991     ## TODO: Relative to @codebase
2992     border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
2993     classid => $HTMLURIAttrChecker,
2994     codebase => $HTMLURIAttrChecker,
2995     codetype => $HTMLIMTAttrChecker,
2996     ## TODO: "RECOMMENDED when |classid| is specified" [HTML4]
2997 wakaba 1.1 data => $HTMLURIAttrChecker,
2998 wakaba 1.70 declare => $GetHTMLBooleanAttrChecker->('declare'),
2999     ## NOTE: "The object MUST be instantiated by a subsequent OBJECT ..."
3000     ## [HTML4] but we don't know how to test this.
3001     hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3002     ## TODO: HTML4 |name|
3003     standby => sub {}, ## NOTE: %Text; in HTML4
3004 wakaba 1.1 type => $HTMLIMTAttrChecker,
3005     usemap => $HTMLUsemapAttrChecker,
3006 wakaba 1.70 vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3007 wakaba 1.1 ## TODO: width
3008     ## TODO: height
3009 wakaba 1.49 }, {
3010     %HTMLAttrStatus,
3011     %HTMLM12NCommonAttrStatus,
3012     align => FEATURE_XHTML10_REC,
3013     archive => FEATURE_M12N10_REC,
3014     border => FEATURE_XHTML10_REC,
3015     classid => FEATURE_M12N10_REC,
3016     codebase => FEATURE_M12N10_REC,
3017     codetype => FEATURE_M12N10_REC,
3018 wakaba 1.50 data => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3019 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
3020     dataformatas => FEATURE_HTML4_REC_RESERVED,
3021     datasrc => FEATURE_HTML4_REC_RESERVED,
3022     declare => FEATURE_M12N10_REC,
3023 wakaba 1.50 height => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3024 wakaba 1.49 hspace => FEATURE_XHTML10_REC,
3025 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3026 wakaba 1.49 name => FEATURE_M12N10_REC,
3027     standby => FEATURE_M12N10_REC,
3028 wakaba 1.50 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3029     type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3030     usemap => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3031 wakaba 1.49 vspace => FEATURE_XHTML10_REC,
3032 wakaba 1.50 width => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3033 wakaba 1.66 })->($self, $item, $element_state);
3034 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'data')) {
3035     unless ($item->{node}->has_attribute_ns (undef, 'type')) {
3036     $self->{onerror}->(node => $item->{node},
3037 wakaba 1.1 type => 'attribute missing:data|type');
3038     }
3039     }
3040 wakaba 1.66
3041     $element_state->{uri_info}->{data}->{type}->{embedded} = 1;
3042     $element_state->{uri_info}->{classid}->{type}->{embedded} = 1;
3043     $element_state->{uri_info}->{codebase}->{type}->{base} = 1;
3044     ## TODO: archive
3045     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
3046 wakaba 1.1 },
3047 wakaba 1.41 ## NOTE: param*, transparent (Prose)
3048     check_child_element => sub {
3049     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3050     $child_is_transparent, $element_state) = @_;
3051     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3052     $self->{onerror}->(node => $child_el,
3053     type => 'element not allowed:minus',
3054     level => $self->{must_level});
3055     $element_state->{has_non_legend} = 1;
3056     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3057     #
3058     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'param') {
3059     if ($element_state->{has_non_param}) {
3060     $self->{onerror}->(node => $child_el,
3061     type => 'element not allowed:prose',
3062     level => $self->{must_level});
3063 wakaba 1.39 }
3064 wakaba 1.41 } else {
3065     $HTMLProseContentChecker{check_child_element}->(@_);
3066     $element_state->{has_non_param} = 1;
3067 wakaba 1.39 }
3068 wakaba 1.25 },
3069 wakaba 1.41 check_child_text => sub {
3070     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3071     if ($has_significant) {
3072     $element_state->{has_non_param} = 1;
3073     }
3074 wakaba 1.42 },
3075     check_end => sub {
3076     my ($self, $item, $element_state) = @_;
3077     if ($element_state->{has_significant}) {
3078 wakaba 1.46 $item->{real_parent_state}->{has_significant} = 1;
3079 wakaba 1.42 } elsif ($item->{node}->manakai_parent_element) {
3080     ## NOTE: Transparent.
3081     } else {
3082     $self->{onerror}->(node => $item->{node},
3083     level => $self->{should_level},
3084     type => 'no significant content');
3085     }
3086     },
3087 wakaba 1.8 ## TODO: Tests for <nest/> in <object/>
3088 wakaba 1.1 };
3089 wakaba 1.41 ## ISSUE: Is |<menu><object data><li>aa</li></object></menu>| conforming?
3090     ## What about |<section><object data><style scoped></style>x</object></section>|?
3091     ## |<section><ins></ins><object data><style scoped></style>x</object></section>|?
3092 wakaba 1.1
3093     $Element->{$HTML_NS}->{param} = {
3094 wakaba 1.40 %HTMLEmptyChecker,
3095 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3096 wakaba 1.40 check_attrs => sub {
3097     my ($self, $item, $element_state) = @_;
3098 wakaba 1.1 $GetHTMLAttrsChecker->({
3099     name => sub { },
3100 wakaba 1.70 type => $HTMLIMTAttrChecker,
3101 wakaba 1.1 value => sub { },
3102 wakaba 1.70 valuetype => $GetHTMLEnumeratedAttrChecker->({
3103     data => 1, ref => 1, object => 1,
3104     }),
3105 wakaba 1.49 }, {
3106     %HTMLAttrStatus,
3107 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3108     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3109 wakaba 1.49 type => FEATURE_M12N10_REC,
3110 wakaba 1.50 value => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3111 wakaba 1.49 valuetype => FEATURE_M12N10_REC,
3112 wakaba 1.66 })->(@_);
3113 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'name')) {
3114     $self->{onerror}->(node => $item->{node},
3115 wakaba 1.1 type => 'attribute missing:name');
3116     }
3117 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'value')) {
3118     $self->{onerror}->(node => $item->{node},
3119 wakaba 1.1 type => 'attribute missing:value');
3120     }
3121     },
3122     };
3123    
3124     $Element->{$HTML_NS}->{video} = {
3125 wakaba 1.40 %HTMLTransparentChecker,
3126 wakaba 1.48 status => FEATURE_HTML5_LC,
3127 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3128 wakaba 1.1 src => $HTMLURIAttrChecker,
3129     ## TODO: start, loopstart, loopend, end
3130     ## ISSUE: they MUST be "value time offset"s. Value?
3131 wakaba 1.11 ## ISSUE: playcount has no conformance creteria
3132 wakaba 1.1 autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
3133     controls => $GetHTMLBooleanAttrChecker->('controls'),
3134 wakaba 1.59 poster => $HTMLURIAttrChecker,
3135 wakaba 1.42 ## TODO: width, height
3136 wakaba 1.50 }, {
3137     %HTMLAttrStatus,
3138     autoplay => FEATURE_HTML5_LC,
3139     controls => FEATURE_HTML5_LC,
3140     end => FEATURE_HTML5_LC,
3141     height => FEATURE_HTML5_LC,
3142     loopend => FEATURE_HTML5_LC,
3143     loopstart => FEATURE_HTML5_LC,
3144     playcount => FEATURE_HTML5_LC,
3145     poster => FEATURE_HTML5_LC,
3146     src => FEATURE_HTML5_LC,
3147     start => FEATURE_HTML5_LC,
3148     width => FEATURE_HTML5_LC,
3149 wakaba 1.1 }),
3150 wakaba 1.42 check_start => sub {
3151     my ($self, $item, $element_state) = @_;
3152     $element_state->{allow_source}
3153     = not $item->{node}->has_attribute_ns (undef, 'src');
3154     $element_state->{has_source} ||= $element_state->{allow_source} * -1;
3155     ## NOTE: It might be set true by |check_element|.
3156 wakaba 1.66
3157     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
3158     $element_state->{uri_info}->{poster}->{type}->{embedded} = 1;
3159 wakaba 1.42 },
3160     check_child_element => sub {
3161     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3162     $child_is_transparent, $element_state) = @_;
3163     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3164     $self->{onerror}->(node => $child_el,
3165     type => 'element not allowed:minus',
3166     level => $self->{must_level});
3167     delete $element_state->{allow_source};
3168     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3169     #
3170     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'source') {
3171 wakaba 1.45 unless ($element_state->{allow_source}) {
3172 wakaba 1.42 $self->{onerror}->(node => $child_el,
3173     type => 'element not allowed:prose',
3174     level => $self->{must_level});
3175     }
3176 wakaba 1.45 $element_state->{has_source} = 1;
3177 wakaba 1.1 } else {
3178 wakaba 1.42 delete $element_state->{allow_source};
3179     $HTMLProseContentChecker{check_child_element}->(@_);
3180     }
3181     },
3182     check_child_text => sub {
3183     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3184     if ($has_significant) {
3185     delete $element_state->{allow_source};
3186     }
3187     $HTMLProseContentChecker{check_child_text}->(@_);
3188     },
3189     check_end => sub {
3190     my ($self, $item, $element_state) = @_;
3191     if ($element_state->{has_source} == -1) {
3192     $self->{onerror}->(node => $item->{node},
3193     type => 'element missing:source',
3194     level => $self->{must_level});
3195 wakaba 1.1 }
3196 wakaba 1.42
3197     $Element->{$HTML_NS}->{object}->{check_end}->(@_);
3198 wakaba 1.1 },
3199     };
3200    
3201     $Element->{$HTML_NS}->{audio} = {
3202 wakaba 1.40 %{$Element->{$HTML_NS}->{video}},
3203 wakaba 1.48 status => FEATURE_HTML5_LC,
3204 wakaba 1.42 check_attrs => $GetHTMLAttrsChecker->({
3205     src => $HTMLURIAttrChecker,
3206     ## TODO: start, loopstart, loopend, end
3207     ## ISSUE: they MUST be "value time offset"s. Value?
3208     ## ISSUE: playcount has no conformance creteria
3209     autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
3210     controls => $GetHTMLBooleanAttrChecker->('controls'),
3211 wakaba 1.50 }, {
3212     %HTMLAttrStatus,
3213     autoplay => FEATURE_HTML5_LC,
3214     controls => FEATURE_HTML5_LC,
3215     end => FEATURE_HTML5_LC,
3216     loopend => FEATURE_HTML5_LC,
3217     loopstart => FEATURE_HTML5_LC,
3218     playcount => FEATURE_HTML5_LC,
3219     src => FEATURE_HTML5_LC,
3220     start => FEATURE_HTML5_LC,
3221 wakaba 1.42 }),
3222 wakaba 1.1 };
3223    
3224     $Element->{$HTML_NS}->{source} = {
3225 wakaba 1.40 %HTMLEmptyChecker,
3226 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3227 wakaba 1.40 check_attrs => sub {
3228     my ($self, $item, $element_state) = @_;
3229 wakaba 1.1 $GetHTMLAttrsChecker->({
3230     src => $HTMLURIAttrChecker,
3231     type => $HTMLIMTAttrChecker,
3232     media => $HTMLMQAttrChecker,
3233 wakaba 1.50 }, {
3234     %HTMLAttrStatus,
3235     media => FEATURE_HTML5_DEFAULT,
3236     src => FEATURE_HTML5_DEFAULT,
3237     type => FEATURE_HTML5_DEFAULT,
3238 wakaba 1.66 })->(@_);
3239 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'src')) {
3240     $self->{onerror}->(node => $item->{node},
3241 wakaba 1.1 type => 'attribute missing:src');
3242     }
3243 wakaba 1.66
3244     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
3245 wakaba 1.1 },
3246     };
3247    
3248     $Element->{$HTML_NS}->{canvas} = {
3249 wakaba 1.40 %HTMLTransparentChecker,
3250 wakaba 1.48 status => FEATURE_HTML5_LC,
3251 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3252 wakaba 1.1 height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3253     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3254 wakaba 1.50 }, {
3255     %HTMLAttrStatus,
3256     height => FEATURE_HTML5_LC,
3257     width => FEATURE_HTML5_LC,
3258 wakaba 1.1 }),
3259     };
3260    
3261     $Element->{$HTML_NS}->{map} = {
3262 wakaba 1.40 %HTMLProseContentChecker,
3263 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3264 wakaba 1.40 check_attrs => sub {
3265     my ($self, $item, $element_state) = @_;
3266 wakaba 1.4 my $has_id;
3267     $GetHTMLAttrsChecker->({
3268     id => sub {
3269     ## NOTE: same as global |id=""|, with |$self->{map}| registeration
3270     my ($self, $attr) = @_;
3271     my $value = $attr->value;
3272     if (length $value > 0) {
3273     if ($self->{id}->{$value}) {
3274     $self->{onerror}->(node => $attr, type => 'duplicate ID');
3275     push @{$self->{id}->{$value}}, $attr;
3276     } else {
3277     $self->{id}->{$value} = [$attr];
3278     }
3279 wakaba 1.1 } else {
3280 wakaba 1.4 ## NOTE: MUST contain at least one character
3281     $self->{onerror}->(node => $attr, type => 'empty attribute value');
3282 wakaba 1.1 }
3283 wakaba 1.4 if ($value =~ /[\x09-\x0D\x20]/) {
3284     $self->{onerror}->(node => $attr, type => 'space in ID');
3285     }
3286     $self->{map}->{$value} ||= $attr;
3287     $has_id = 1;
3288     },
3289 wakaba 1.70 ## TODO: HTML4 |name|
3290 wakaba 1.49 }, {
3291     %HTMLAttrStatus,
3292 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3293     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3294     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3295     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3296 wakaba 1.49 name => FEATURE_M12N10_REC_DEPRECATED,
3297 wakaba 1.50 onclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3298     ondblclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3299     onmousedown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3300     onmouseup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3301     onmouseover => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3302     onmousemove => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3303     onmouseout => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3304     onkeypress => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3305     onkeydown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3306     onkeyup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3307     title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3308 wakaba 1.66 })->(@_);
3309 wakaba 1.40 $self->{onerror}->(node => $item->{node}, type => 'attribute missing:id')
3310 wakaba 1.4 unless $has_id;
3311     },
3312 wakaba 1.59 check_start => sub {
3313     my ($self, $item, $element_state) = @_;
3314     $element_state->{in_map_original} = $self->{flag}->{in_map};
3315     $self->{flag}->{in_map} = 1;
3316     },
3317     check_end => sub {
3318     my ($self, $item, $element_state) = @_;
3319     delete $self->{flag}->{in_map} unless $element_state->{in_map_original};
3320     $HTMLProseContentChecker{check_end}->(@_);
3321     },
3322 wakaba 1.1 };
3323    
3324     $Element->{$HTML_NS}->{area} = {
3325 wakaba 1.40 %HTMLEmptyChecker,
3326 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3327 wakaba 1.40 check_attrs => sub {
3328     my ($self, $item, $element_state) = @_;
3329 wakaba 1.1 my %attr;
3330     my $coords;
3331 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
3332 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
3333     $attr_ns = '' unless defined $attr_ns;
3334     my $attr_ln = $attr->manakai_local_name;
3335     my $checker;
3336     if ($attr_ns eq '') {
3337     $checker = {
3338 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
3339 wakaba 1.1 alt => sub { },
3340     ## NOTE: |alt| value has no conformance creteria.
3341     shape => $GetHTMLEnumeratedAttrChecker->({
3342     circ => -1, circle => 1,
3343     default => 1,
3344     poly => 1, polygon => -1,
3345     rect => 1, rectangle => -1,
3346     }),
3347     coords => sub {
3348     my ($self, $attr) = @_;
3349     my $value = $attr->value;
3350     if ($value =~ /\A-?[0-9]+(?>,-?[0-9]+)*\z/) {
3351     $coords = [split /,/, $value];
3352     } else {
3353     $self->{onerror}->(node => $attr,
3354     type => 'coords:syntax error');
3355     }
3356     },
3357 wakaba 1.70 nohref => $GetHTMLBooleanAttrChecker->('nohref'),
3358     target => $HTMLTargetAttrChecker,
3359 wakaba 1.1 href => $HTMLURIAttrChecker,
3360     ping => $HTMLSpaceURIsAttrChecker,
3361 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) },
3362 wakaba 1.1 media => $HTMLMQAttrChecker,
3363     hreflang => $HTMLLanguageTagAttrChecker,
3364     type => $HTMLIMTAttrChecker,
3365     }->{$attr_ln};
3366     if ($checker) {
3367     $attr{$attr_ln} = $attr;
3368     } else {
3369     $checker = $HTMLAttrChecker->{$attr_ln};
3370     }
3371     }
3372     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
3373     || $AttrChecker->{$attr_ns}->{''};
3374 wakaba 1.62
3375     my $status = {
3376     %HTMLAttrStatus,
3377     %HTMLM12NCommonAttrStatus,
3378     accesskey => FEATURE_M12N10_REC,
3379     alt => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3380     coords => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3381     href => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3382     hreflang => FEATURE_HTML5_DEFAULT,
3383     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3384     media => FEATURE_HTML5_DEFAULT,
3385     nohref => FEATURE_M12N10_REC,
3386     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3387     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3388     ping => FEATURE_HTML5_DEFAULT,
3389     rel => FEATURE_HTML5_DEFAULT,
3390     shape => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3391     tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3392     target => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3393     type => FEATURE_HTML5_DEFAULT,
3394     }->{$attr_ln};
3395    
3396 wakaba 1.1 if ($checker) {
3397 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
3398 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
3399 wakaba 1.54 #
3400 wakaba 1.1 } else {
3401     $self->{onerror}->(node => $attr, level => 'unsupported',
3402     type => 'attribute');
3403     ## ISSUE: No comformance createria for unknown attributes in the spec
3404     }
3405 wakaba 1.49
3406     if ($attr_ns eq '') {
3407 wakaba 1.62 $self->_attr_status_info ($attr, $status);
3408 wakaba 1.49 }
3409 wakaba 1.1 }
3410    
3411     if (defined $attr{href}) {
3412 wakaba 1.4 $self->{has_hyperlink_element} = 1;
3413 wakaba 1.1 unless (defined $attr{alt}) {
3414 wakaba 1.40 $self->{onerror}->(node => $item->{node},
3415 wakaba 1.1 type => 'attribute missing:alt');
3416     }
3417     } else {
3418     for (qw/target ping rel media hreflang type alt/) {
3419     if (defined $attr{$_}) {
3420     $self->{onerror}->(node => $attr{$_},
3421     type => 'attribute not allowed');
3422     }
3423     }
3424     }
3425    
3426     my $shape = 'rectangle';
3427     if (defined $attr{shape}) {
3428     $shape = {
3429     circ => 'circle', circle => 'circle',
3430     default => 'default',
3431     poly => 'polygon', polygon => 'polygon',
3432     rect => 'rectangle', rectangle => 'rectangle',
3433     }->{lc $attr{shape}->value} || 'rectangle';
3434     ## TODO: ASCII lowercase?
3435     }
3436    
3437     if ($shape eq 'circle') {
3438     if (defined $attr{coords}) {
3439     if (defined $coords) {
3440     if (@$coords == 3) {
3441     if ($coords->[2] < 0) {
3442     $self->{onerror}->(node => $attr{coords},
3443     type => 'coords:out of range:2');
3444     }
3445     } else {
3446     $self->{onerror}->(node => $attr{coords},
3447     type => 'coords:number:3:'.@$coords);
3448     }
3449     } else {
3450     ## NOTE: A syntax error has been reported.
3451     }
3452     } else {
3453 wakaba 1.40 $self->{onerror}->(node => $item->{node},
3454 wakaba 1.1 type => 'attribute missing:coords');
3455     }
3456     } elsif ($shape eq 'default') {
3457     if (defined $attr{coords}) {
3458     $self->{onerror}->(node => $attr{coords},
3459     type => 'attribute not allowed');
3460     }
3461     } elsif ($shape eq 'polygon') {
3462     if (defined $attr{coords}) {
3463     if (defined $coords) {
3464     if (@$coords >= 6) {
3465     unless (@$coords % 2 == 0) {
3466     $self->{onerror}->(node => $attr{coords},
3467     type => 'coords:number:even:'.@$coords);
3468     }
3469     } else {
3470     $self->{onerror}->(node => $attr{coords},
3471     type => 'coords:number:>=6:'.@$coords);
3472     }
3473     } else {
3474     ## NOTE: A syntax error has been reported.
3475     }
3476     } else {
3477 wakaba 1.40 $self->{onerror}->(node => $item->{node},
3478 wakaba 1.1 type => 'attribute missing:coords');
3479     }
3480     } elsif ($shape eq 'rectangle') {
3481     if (defined $attr{coords}) {
3482     if (defined $coords) {
3483     if (@$coords == 4) {
3484     unless ($coords->[0] < $coords->[2]) {
3485     $self->{onerror}->(node => $attr{coords},
3486     type => 'coords:out of range:0');
3487     }
3488     unless ($coords->[1] < $coords->[3]) {
3489     $self->{onerror}->(node => $attr{coords},
3490     type => 'coords:out of range:1');
3491     }
3492     } else {
3493     $self->{onerror}->(node => $attr{coords},
3494     type => 'coords:number:4:'.@$coords);
3495     }
3496     } else {
3497     ## NOTE: A syntax error has been reported.
3498     }
3499     } else {
3500 wakaba 1.40 $self->{onerror}->(node => $item->{node},
3501 wakaba 1.1 type => 'attribute missing:coords');
3502     }
3503     }
3504 wakaba 1.66
3505     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
3506 wakaba 1.1 },
3507 wakaba 1.59 check_start => sub {
3508     my ($self, $item, $element_state) = @_;
3509     unless ($self->{flag}->{in_map} or
3510     not $item->{node}->manakai_parent_element) {
3511     $self->{onerror}->(node => $item->{node},
3512     type => 'element not allowed:area',
3513     level => $self->{must_level});
3514     }
3515     },
3516 wakaba 1.1 };
3517    
3518     $Element->{$HTML_NS}->{table} = {
3519 wakaba 1.40 %HTMLChecker,
3520 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3521 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
3522 wakaba 1.70 ## TODO: HTML4 |cellspacing|, |cellpadding|
3523 wakaba 1.69 frame => $GetHTMLEnumeratedAttrChecker->({
3524     void => 1, above => 1, below => 1, hsides => 1, vsides => 1,
3525     lhs => 1, rhs => 1, box => 1, border => 1,
3526     }),
3527     rules => $GetHTMLEnumeratedAttrChecker->({
3528     none => 1, groups => 1, rows => 1, cols => 1, all => 1,
3529     }),
3530     summary => sub {}, ## NOTE: %Text; in HTML4.
3531     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## %Pixels;
3532     }, {
3533 wakaba 1.49 %HTMLAttrStatus,
3534     %HTMLM12NCommonAttrStatus,
3535     align => FEATURE_M12N10_REC_DEPRECATED,
3536     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
3537     border => FEATURE_M12N10_REC,
3538     cellpadding => FEATURE_M12N10_REC,
3539     cellspacing => FEATURE_M12N10_REC,
3540 wakaba 1.61 cols => FEATURE_RFC1942,
3541 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
3542     dataformatas => FEATURE_HTML4_REC_RESERVED,
3543     datapagesize => FEATURE_M12N10_REC,
3544     datasrc => FEATURE_HTML4_REC_RESERVED,
3545     frame => FEATURE_M12N10_REC,
3546 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3547 wakaba 1.49 rules => FEATURE_M12N10_REC,
3548     summary => FEATURE_M12N10_REC,
3549     width => FEATURE_M12N10_REC,
3550     }),
3551 wakaba 1.40 check_start => sub {
3552     my ($self, $item, $element_state) = @_;
3553     $element_state->{phase} = 'before caption';
3554 wakaba 1.66
3555     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
3556 wakaba 1.40 },
3557     check_child_element => sub {
3558     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3559     $child_is_transparent, $element_state) = @_;
3560     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3561     $self->{onerror}->(node => $child_el,
3562     type => 'element not allowed:minus',
3563     level => $self->{must_level});
3564     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3565     #
3566     } elsif ($element_state->{phase} eq 'in tbodys') {
3567     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
3568     #$element_state->{phase} = 'in tbodys';
3569     } elsif (not $element_state->{has_tfoot} and
3570     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
3571     $element_state->{phase} = 'after tfoot';
3572     $element_state->{has_tfoot} = 1;
3573     } else {
3574     $self->{onerror}->(node => $child_el, type => 'element not allowed');
3575     }
3576     } elsif ($element_state->{phase} eq 'in trs') {
3577     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
3578     #$element_state->{phase} = 'in trs';
3579     } elsif (not $element_state->{has_tfoot} and
3580     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
3581     $element_state->{phase} = 'after tfoot';
3582     $element_state->{has_tfoot} = 1;
3583     } else {
3584     $self->{onerror}->(node => $child_el, type => 'element not allowed');
3585     }
3586     } elsif ($element_state->{phase} eq 'after thead') {
3587     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
3588     $element_state->{phase} = 'in tbodys';
3589     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
3590     $element_state->{phase} = 'in trs';
3591     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
3592     $element_state->{phase} = 'in tbodys';
3593     $element_state->{has_tfoot} = 1;
3594     } else {
3595     $self->{onerror}->(node => $child_el, type => 'element not allowed');
3596     }
3597     } elsif ($element_state->{phase} eq 'in colgroup') {
3598     if ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
3599     $element_state->{phase} = 'in colgroup';
3600     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
3601     $element_state->{phase} = 'after thead';
3602     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
3603     $element_state->{phase} = 'in tbodys';
3604     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
3605     $element_state->{phase} = 'in trs';
3606     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
3607     $element_state->{phase} = 'in tbodys';
3608     $element_state->{has_tfoot} = 1;
3609     } else {
3610     $self->{onerror}->(node => $child_el, type => 'element not allowed');
3611     }
3612     } elsif ($element_state->{phase} eq 'before caption') {
3613     if ($child_nsuri eq $HTML_NS and $child_ln eq 'caption') {
3614     $element_state->{phase} = 'in colgroup';
3615     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
3616     $element_state->{phase} = 'in colgroup';
3617     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
3618     $element_state->{phase} = 'after thead';
3619     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
3620     $element_state->{phase} = 'in tbodys';
3621     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
3622     $element_state->{phase} = 'in trs';
3623     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
3624     $element_state->{phase} = 'in tbodys';
3625     $element_state->{has_tfoot} = 1;
3626     } else {
3627     $self->{onerror}->(node => $child_el, type => 'element not allowed');
3628     }
3629     } elsif ($element_state->{phase} eq 'after tfoot') {
3630     $self->{onerror}->(node => $child_el, type => 'element not allowed');
3631     } else {
3632     die "check_child_element: Bad |table| phase: $element_state->{phase}";
3633     }
3634     },
3635     check_child_text => sub {
3636     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3637     if ($has_significant) {
3638     $self->{onerror}->(node => $child_node, type => 'character not allowed');
3639 wakaba 1.1 }
3640 wakaba 1.40 },
3641     check_end => sub {
3642     my ($self, $item, $element_state) = @_;
3643 wakaba 1.1
3644     ## Table model errors
3645     require Whatpm::HTMLTable;
3646 wakaba 1.40 Whatpm::HTMLTable->form_table ($item->{node}, sub {
3647 wakaba 1.1 my %opt = @_;
3648     $self->{onerror}->(type => 'table:'.$opt{type}, node => $opt{node});
3649     });
3650 wakaba 1.40 push @{$self->{return}->{table}}, $item->{node};
3651 wakaba 1.1
3652 wakaba 1.40 $HTMLChecker{check_end}->(@_);
3653 wakaba 1.1 },
3654     };
3655    
3656     $Element->{$HTML_NS}->{caption} = {
3657 wakaba 1.40 %HTMLPhrasingContentChecker,
3658 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3659 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
3660     align => $GetHTMLEnumeratedAttrChecker->({
3661     top => 1, bottom => 1, left => 1, right => 1,
3662     }),
3663     }, {
3664 wakaba 1.49 %HTMLAttrStatus,
3665     %HTMLM12NCommonAttrStatus,
3666     align => FEATURE_M12N10_REC_DEPRECATED,
3667 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3668 wakaba 1.49 }),
3669 wakaba 1.1 };
3670    
3671 wakaba 1.69 my %cellalign = (
3672     ## HTML4 %cellhalign;
3673 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
3674     left => 1, center => 1, right => 1, justify => 1, char => 1,
3675     }),
3676     char => sub {
3677     my ($self, $attr) = @_;
3678 wakaba 1.69
3679 wakaba 1.70 ## NOTE: "character" or |%Character;| in HTML4.
3680    
3681     my $value = $attr->value;
3682     if (length $value != 1) {
3683     $self->{onerror}->(node => $attr, type => 'char:syntax error',
3684     level => $self->{fact_level}); ## TODO: type
3685     }
3686     },
3687     ## TODO: HTML4 |charoff|
3688 wakaba 1.69 ## HTML4 %cellvalign;
3689 wakaba 1.70 valign => $GetHTMLEnumeratedAttrChecker->({
3690     top => 1, middle => 1, bottom => 1, baseline => 1,
3691     }),
3692 wakaba 1.69 );
3693    
3694 wakaba 1.1 $Element->{$HTML_NS}->{colgroup} = {
3695 wakaba 1.40 %HTMLEmptyChecker,
3696 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3697 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3698 wakaba 1.69 %cellalign,
3699 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
3700     ## NOTE: Defined only if "the |colgroup| element contains no |col| elements"
3701     ## TODO: "attribute not supported" if |col|.
3702     ## ISSUE: MUST NOT if any |col|?
3703     ## ISSUE: MUST NOT for |<colgroup span="1"><any><col/></any></colgroup>| (though non-conforming)?
3704 wakaba 1.49 }, {
3705     %HTMLAttrStatus,
3706     %HTMLM12NCommonAttrStatus,
3707     align => FEATURE_M12N10_REC,
3708     char => FEATURE_M12N10_REC,
3709     charoff => FEATURE_M12N10_REC,
3710 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3711     span => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3712 wakaba 1.49 valign => FEATURE_M12N10_REC,
3713     width => FEATURE_M12N10_REC,
3714 wakaba 1.1 }),
3715 wakaba 1.40 check_child_element => sub {
3716     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3717     $child_is_transparent, $element_state) = @_;
3718     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3719     $self->{onerror}->(node => $child_el,
3720     type => 'element not allowed:minus',
3721     level => $self->{must_level});
3722     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3723     #
3724     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'col') {
3725     #
3726     } else {
3727     $self->{onerror}->(node => $child_el, type => 'element not allowed');
3728     }
3729     },
3730     check_child_text => sub {
3731     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3732     if ($has_significant) {
3733     $self->{onerror}->(node => $child_node, type => 'character not allowed');
3734 wakaba 1.1 }
3735     },
3736     };
3737    
3738     $Element->{$HTML_NS}->{col} = {
3739 wakaba 1.40 %HTMLEmptyChecker,
3740 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3741 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3742 wakaba 1.69 %cellalign,
3743 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
3744 wakaba 1.49 }, {
3745     %HTMLAttrStatus,
3746     %HTMLM12NCommonAttrStatus,
3747     align => FEATURE_M12N10_REC,
3748     char => FEATURE_M12N10_REC,
3749     charoff => FEATURE_M12N10_REC,
3750 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3751     span => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3752 wakaba 1.49 valign => FEATURE_M12N10_REC,
3753     width => FEATURE_M12N10_REC,
3754 wakaba 1.1 }),
3755     };
3756    
3757     $Element->{$HTML_NS}->{tbody} = {
3758 wakaba 1.40 %HTMLChecker,
3759 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3760 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
3761     %cellalign,
3762     }, {
3763 wakaba 1.49 %HTMLAttrStatus,
3764     %HTMLM12NCommonAttrStatus,
3765     align => FEATURE_M12N10_REC,
3766     char => FEATURE_M12N10_REC,
3767     charoff => FEATURE_M12N10_REC,
3768 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3769 wakaba 1.49 valign => FEATURE_M12N10_REC,
3770     }),
3771 wakaba 1.40 check_child_element => sub {
3772     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3773     $child_is_transparent, $element_state) = @_;
3774     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3775     $self->{onerror}->(node => $child_el,
3776     type => 'element not allowed:minus',
3777     level => $self->{must_level});
3778     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3779     #
3780     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
3781     $element_state->{has_tr} = 1;
3782     } else {
3783     $self->{onerror}->(node => $child_el, type => 'element not allowed');
3784     }
3785     },
3786     check_child_text => sub {
3787     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3788     if ($has_significant) {
3789     $self->{onerror}->(node => $child_node, type => 'character not allowed');
3790 wakaba 1.1 }
3791 wakaba 1.40 },
3792     check_end => sub {
3793     my ($self, $item, $element_state) = @_;
3794     unless ($element_state->{has_tr}) {
3795     $self->{onerror}->(node => $item->{node},
3796     type => 'child element missing:tr');
3797 wakaba 1.1 }
3798 wakaba 1.40
3799     $HTMLChecker{check_end}->(@_);
3800 wakaba 1.1 },
3801     };
3802    
3803     $Element->{$HTML_NS}->{thead} = {
3804 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
3805 wakaba 1.1 };
3806    
3807     $Element->{$HTML_NS}->{tfoot} = {
3808 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
3809 wakaba 1.1 };
3810    
3811     $Element->{$HTML_NS}->{tr} = {
3812 wakaba 1.40 %HTMLChecker,
3813 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3814 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
3815     %cellalign,
3816     bgcolor => $HTMLColorAttrChecker,
3817     }, {
3818 wakaba 1.49 %HTMLAttrStatus,
3819     %HTMLM12NCommonAttrStatus,
3820     align => FEATURE_M12N10_REC,
3821     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
3822     char => FEATURE_M12N10_REC,
3823     charoff => FEATURE_M12N10_REC,
3824 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3825 wakaba 1.49 valign => FEATURE_M12N10_REC,
3826     }),
3827 wakaba 1.40 check_child_element => sub {
3828     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3829     $child_is_transparent, $element_state) = @_;
3830     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3831     $self->{onerror}->(node => $child_el,
3832     type => 'element not allowed:minus',
3833     level => $self->{must_level});
3834     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3835     #
3836     } elsif ($child_nsuri eq $HTML_NS and
3837     ($child_ln eq 'td' or $child_ln eq 'th')) {
3838     $element_state->{has_cell} = 1;
3839     } else {
3840     $self->{onerror}->(node => $child_el, type => 'element not allowed');
3841     }
3842     },
3843     check_child_text => sub {
3844     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3845     if ($has_significant) {
3846     $self->{onerror}->(node => $child_node, type => 'character not allowed');
3847 wakaba 1.1 }
3848 wakaba 1.40 },
3849     check_end => sub {
3850     my ($self, $item, $element_state) = @_;
3851     unless ($element_state->{has_cell}) {
3852     $self->{onerror}->(node => $item->{node},
3853     type => 'child element missing:td|th');
3854 wakaba 1.1 }
3855 wakaba 1.40
3856     $HTMLChecker{check_end}->(@_);
3857 wakaba 1.1 },
3858     };
3859    
3860     $Element->{$HTML_NS}->{td} = {
3861 wakaba 1.40 %HTMLProseContentChecker,
3862 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3863 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3864 wakaba 1.69 %cellalign,
3865     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
3866     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
3867     bgcolor => $HTMLColorAttrChecker,
3868 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
3869 wakaba 1.70 ## TODO: HTML5 |headers|
3870 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
3871 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
3872 wakaba 1.69 scope => $GetHTMLEnumeratedAttrChecker
3873     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
3874 wakaba 1.49 }, {
3875     %HTMLAttrStatus,
3876     %HTMLM12NCommonAttrStatus,
3877     abbr => FEATURE_M12N10_REC,
3878     align => FEATURE_M12N10_REC,
3879     axis => FEATURE_M12N10_REC,
3880     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
3881     char => FEATURE_M12N10_REC,
3882     charoff => FEATURE_M12N10_REC,
3883 wakaba 1.50 colspan => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3884 wakaba 1.49 headers => FEATURE_M12N10_REC,
3885     height => FEATURE_M12N10_REC_DEPRECATED,
3886 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3887 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
3888 wakaba 1.50 rowspan => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3889 wakaba 1.49 scope => FEATURE_M12N10_REC,
3890     valign => FEATURE_M12N10_REC,
3891     width => FEATURE_M12N10_REC_DEPRECATED,
3892 wakaba 1.1 }),
3893     };
3894    
3895     $Element->{$HTML_NS}->{th} = {
3896 wakaba 1.40 %HTMLPhrasingContentChecker,
3897 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3898 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3899 wakaba 1.69 %cellalign,
3900     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
3901     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
3902     bgcolor => $HTMLColorAttrChecker,
3903 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
3904 wakaba 1.70 ## TODO: HTML5 |headers|
3905 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
3906 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
3907     scope => $GetHTMLEnumeratedAttrChecker
3908     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
3909 wakaba 1.49 }, {
3910     %HTMLAttrStatus,
3911     %HTMLM12NCommonAttrStatus,
3912     abbr => FEATURE_M12N10_REC,
3913     align => FEATURE_M12N10_REC,
3914     axis => FEATURE_M12N10_REC,
3915     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
3916     char => FEATURE_M12N10_REC,
3917     charoff => FEATURE_M12N10_REC,
3918 wakaba 1.50 colspan => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3919 wakaba 1.49 headers => FEATURE_M12N10_REC,
3920     height => FEATURE_M12N10_REC_DEPRECATED,
3921 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3922 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
3923 wakaba 1.50 rowspan => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3924     scope => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3925 wakaba 1.49 valign => FEATURE_M12N10_REC,
3926     width => FEATURE_M12N10_REC_DEPRECATED,
3927 wakaba 1.1 }),
3928     };
3929    
3930 wakaba 1.52 my $AttrCheckerNotImplemented = sub {
3931     my ($self, $attr) = @_;
3932     $self->{onerror}->(node => $attr, level => 'unsupported',
3933     type => 'attribute');
3934     };
3935    
3936     $Element->{$HTML_NS}->{form} = {
3937 wakaba 1.56 %HTMLProseContentChecker, ## NOTE: Flow* [WF2]
3938     ## TODO: form in form is allowed in XML [WF2]
3939 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
3940     check_attrs => $GetHTMLAttrsChecker->({
3941 wakaba 1.56 accept => $AttrCheckerNotImplemented, ## TODO: ContentTypes [WF2]
3942 wakaba 1.52 'accept-charset' => $AttrCheckerNotImplemented, ## TODO: Charsets
3943     action => $HTMLURIAttrChecker, ## TODO: "User agent behavior for a value other than HTTP URI is undefined" [HTML4]
3944 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
3945     enctype => $HTMLIMTAttrChecker, ## TODO: "multipart/form-data" should be used when type=file is used [HTML4] ## TODO: MUST NOT parameter [WF2]
3946     method => $GetHTMLEnumeratedAttrChecker->({
3947     get => 1, post => 1, put => 1, delete => 1,
3948     }),
3949 wakaba 1.52 ## NOTE: "get" SHOULD be used for idempotent submittion,
3950     ## "post" SHOULD be used otherwise [HTML4]. This cannot be tested.
3951     name => sub { }, # CDATA in HTML4 ## TODO: must be same as |id| (informative!) [XHTML10]
3952 wakaba 1.56 onreceived => $HTMLEventHandlerAttrChecker,
3953     replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
3954 wakaba 1.52 target => $HTMLTargetAttrChecker,
3955     ## TODO: Warn for combination whose behavior is not defined.
3956     }, {
3957     %HTMLAttrStatus,
3958     %HTMLM12NCommonAttrStatus,
3959 wakaba 1.56 accept => FEATURE_WF2 | FEATURE_M12N10_REC,
3960 wakaba 1.52 'accept-charset' => FEATURE_M12N10_REC,
3961 wakaba 1.56 action => FEATURE_WF2 | FEATURE_M12N10_REC,
3962     data => FEATURE_WF2,
3963     enctype => FEATURE_WF2 | FEATURE_M12N10_REC,
3964 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3965 wakaba 1.56 method => FEATURE_WF2 | FEATURE_M12N10_REC,
3966 wakaba 1.52 name => FEATURE_M12N10_REC_DEPRECATED,
3967 wakaba 1.56 onreceived => FEATURE_WF2,
3968 wakaba 1.52 onreset => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3969     onsubmit => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3970 wakaba 1.56 replace => FEATURE_WF2,
3971 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
3972     sdasuff => FEATURE_HTML20_RFC,
3973 wakaba 1.52 target => FEATURE_M12N10_REC,
3974     }),
3975     ## TODO: Tests
3976     ## TODO: Tests for <nest/> in <form>
3977 wakaba 1.66 check_start => sub {
3978     my ($self, $item, $element_state) = @_;
3979    
3980     $element_state->{uri_info}->{action}->{type}->{action} = 1;
3981     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
3982     },
3983 wakaba 1.52 };
3984    
3985     $Element->{$HTML_NS}->{fieldset} = {
3986     %HTMLProseContentChecker, ## NOTE: legend, %Flow; ## TODO: legend
3987     status => FEATURE_WF2 | FEATURE_M12N10_REC,
3988 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
3989     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
3990     ## TODO: form [WF2]
3991     }, {
3992 wakaba 1.52 %HTMLAttrStatus,
3993     %HTMLM12NCommonAttrStatus,
3994 wakaba 1.56 disabled => FEATURE_WF2,
3995     form => FEATURE_WF2,
3996 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3997     }),
3998     ## TODO: Tests
3999     ## TODO: Tests for <nest/> in <fieldset>
4000     };
4001    
4002     $Element->{$HTML_NS}->{input} = {
4003 wakaba 1.56 %HTMLEmptyChecker, ## MUST [WF2]
4004 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
4005     check_attrs => $GetHTMLAttrsChecker->({
4006 wakaba 1.56 accept => $AttrCheckerNotImplemented, ## TODO: ContentTypes [WF2]
4007 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4008 wakaba 1.56 action => $HTMLURIAttrChecker,
4009 wakaba 1.52 align => $GetHTMLEnumeratedAttrChecker->({
4010     top => 1, middle => 1, bottom => 1, left => 1, right => 1,
4011     }),
4012     alt => sub {}, ## NOTE: Text [M12N] ## TODO: |alt| should be provided for |type=image| [HTML4]
4013     ## NOTE: HTML4 has a "should" for accessibility, which cannot be tested
4014     ## here.
4015 wakaba 1.56 autocomplete => $GetHTMLEnumeratedAttrChecker->({on => 1, off => 1}),
4016     autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
4017 wakaba 1.52 checked => $GetHTMLBooleanAttrChecker->('checked'),
4018     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4019 wakaba 1.56 enctype => $HTMLIMTAttrChecker,
4020     ## TODO: form [WF2]
4021     ## TODO: inputmode [WF2]
4022 wakaba 1.52 ismap => $GetHTMLBooleanAttrChecker->('ismap'),
4023 wakaba 1.56 ## TODO: list [WF2]
4024     ## TODO: max [WF2]
4025 wakaba 1.52 maxlength => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4026 wakaba 1.56 method => $GetHTMLEnumeratedAttrChecker->({
4027     get => 1, post => 1, put => 1, delete => 1,
4028     }),
4029     ## TODO: min [WF2]
4030 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
4031     readonly => $GetHTMLBooleanAttrChecker->('readonly'),
4032 wakaba 1.56 replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
4033     required => $GetHTMLBooleanAttrChecker->('required'),
4034 wakaba 1.52 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4035     src => $HTMLURIAttrChecker,
4036 wakaba 1.56 ## TODO: step [WF2]
4037     target => $HTMLTargetAttrChecker,
4038     ## TODO: template
4039 wakaba 1.52 type => $GetHTMLEnumeratedAttrChecker->({
4040     text => 1, password => 1, checkbox => 1, radio => 1, submit => 1,
4041     reset => 1, file => 1, hidden => 1, image => 1, button => 1,
4042 wakaba 1.56 ## [WF2]
4043     datatime => 1, 'datetime-local' => 1, date => 1, month => 1, week => 1,
4044     time => 1, number => 1, range => 1, email => 1, url => 1,
4045     add => 1, remove => 1, 'move-up' => 1, 'move-down' => 1,
4046 wakaba 1.52 }),
4047     usemap => $HTMLUsemapAttrChecker,
4048 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]
4049     ## TODO: "authors should ensure that in each set of radio buttons that one is initially "on"." [HTML4] [WF2]
4050 wakaba 1.52 }, {
4051     %HTMLAttrStatus,
4052     %HTMLM12NCommonAttrStatus,
4053 wakaba 1.56 accept => FEATURE_WF2 | FEATURE_M12N10_REC,
4054 wakaba 1.61 'accept-charset' => FEATURE_HTML2X_RFC,
4055 wakaba 1.52 accesskey => FEATURE_M12N10_REC,
4056 wakaba 1.56 action => FEATURE_WF2,
4057 wakaba 1.52 align => FEATURE_M12N10_REC_DEPRECATED,
4058     alt => FEATURE_M12N10_REC,
4059 wakaba 1.56 autocomplete => FEATURE_WF2,
4060     autofocus => FEATURE_WF2,
4061 wakaba 1.52 checked => FEATURE_M12N10_REC,
4062     datafld => FEATURE_HTML4_REC_RESERVED,
4063     dataformatas => FEATURE_HTML4_REC_RESERVED,
4064     datasrc => FEATURE_HTML4_REC_RESERVED,
4065 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
4066 wakaba 1.65 enctype => FEATURE_WF2,
4067 wakaba 1.56 form => FEATURE_WF2,
4068     inputmode => FEATURE_WF2 | FEATURE_XHTMLBASIC11_CR,
4069 wakaba 1.52 ismap => FEATURE_M12N10_REC,
4070     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4071 wakaba 1.56 list => FEATURE_WF2,
4072     max => FEATURE_WF2,
4073     maxlength => FEATURE_WF2 | FEATURE_M12N10_REC,
4074     method => FEATURE_WF2,
4075     min => FEATURE_WF2,
4076 wakaba 1.52 name => FEATURE_M12N10_REC,
4077     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4078     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4079     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4080     onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4081 wakaba 1.56 readonly => FEATURE_WF2 | FEATURE_M12N10_REC,
4082 wakaba 1.65 replace => FEATURE_WF2,
4083 wakaba 1.56 required => FEATURE_WF2,
4084 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
4085 wakaba 1.56 size => FEATURE_WF2_DEPRECATED | FEATURE_M12N10_REC,
4086 wakaba 1.52 src => FEATURE_M12N10_REC,
4087 wakaba 1.56 step => FEATURE_WF2,
4088 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4089 wakaba 1.65 target => FEATURE_WF2,
4090 wakaba 1.56 template => FEATURE_WF2,
4091 wakaba 1.52 type => FEATURE_M12N10_REC,
4092     usemap => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC,
4093     value => FEATURE_M12N10_REC,
4094     }),
4095     ## TODO: Tests
4096     ## TODO: Tests for <nest/> in <input>
4097 wakaba 1.66 check_start => sub {
4098     my ($self, $item, $element_state) = @_;
4099    
4100     $element_state->{uri_info}->{action}->{type}->{action} = 1;
4101     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4102     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4103     },
4104 wakaba 1.52 };
4105    
4106 wakaba 1.56 ## TODO: Form |name| attributes: MUST NOT conflict with RFC 3106 [WF2]
4107    
4108 wakaba 1.52 $Element->{$HTML_NS}->{button} = {
4109     %HTMLProseContentChecker, ## NOTE: %Flow; - something [XHTML10]
4110     ## TODO: -A|%formctrl;|form|fieldset [HTML4]
4111     ## TODO: image map (img) in |button| is "illegal" [HTML4].
4112     status => FEATURE_WF2 | FEATURE_M12N10_REC,
4113     check_attrs => $GetHTMLAttrsChecker->({
4114 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4115 wakaba 1.56 action => $HTMLURIAttrChecker,
4116     autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
4117 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4118 wakaba 1.56 ## TODO: form [WF2]
4119     method => $GetHTMLEnumeratedAttrChecker->({
4120     get => 1, post => 1, put => 1, delete => 1,
4121     }),
4122 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
4123 wakaba 1.56 oninvalid => $HTMLEventHandlerAttrChecker,
4124     replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
4125     target => $HTMLTargetAttrChecker,
4126     ## TODO: template [WF2]
4127 wakaba 1.52 type => $GetHTMLEnumeratedAttrChecker->({
4128     button => 1, submit => 1, reset => 1,
4129     }),
4130     value => sub {}, ## NOTE: CDATA [M12N]
4131     }, {
4132     %HTMLAttrStatus,
4133     %HTMLM12NCommonAttrStatus,
4134     accesskey => FEATURE_M12N10_REC,
4135 wakaba 1.56 action => FEATURE_WF2,
4136     autofocus => FEATURE_WF2,
4137 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
4138     dataformatas => FEATURE_HTML4_REC_RESERVED,
4139     datasrc => FEATURE_HTML4_REC_RESERVED,
4140 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
4141     enctype => FEATURE_WF2,
4142     form => FEATURE_WF2,
4143 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4144 wakaba 1.56 method => FEATURE_WF2,
4145 wakaba 1.52 name => FEATURE_M12N10_REC,
4146     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4147     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4148 wakaba 1.56 oninvalid => FEATURE_WF2,
4149     replace => FEATURE_WF2,
4150 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4151 wakaba 1.56 target => FEATURE_WF2,
4152     template => FEATURE_WF2,
4153 wakaba 1.52 type => FEATURE_M12N10_REC,
4154     value => FEATURE_M12N10_REC,
4155     }),
4156     ## TODO: Tests
4157     ## TODO: Tests for <nest/> in <button>
4158 wakaba 1.66 check_start => sub {
4159     my ($self, $item, $element_state) = @_;
4160    
4161     $element_state->{uri_info}->{action}->{type}->{action} = 1;
4162     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4163     },
4164 wakaba 1.52 };
4165    
4166     $Element->{$HTML_NS}->{label} = {
4167     %HTMLPhrasingContentChecker, ## NOTE: %Inline - label [XHTML10] ## TODO: -label
4168 wakaba 1.56 ## TODO: At most one form control [WF2]
4169 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
4170     check_attrs => $GetHTMLAttrsChecker->({
4171 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4172 wakaba 1.52 for => $AttrCheckerNotImplemented, ## TODO: IDREF ## TODO: Must be |id| of control [HTML4] ## TODO: Or, "may only contain one control element"
4173     }, {
4174     %HTMLAttrStatus,
4175     %HTMLM12NCommonAttrStatus,
4176 wakaba 1.56 accesskey => FEATURE_WF2 | FEATURE_M12N10_REC,
4177 wakaba 1.52 for => FEATURE_M12N10_REC,
4178     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4179     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4180     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4181     }),
4182     ## TODO: Tests
4183     ## TODO: Tests for <nest/> in <label>
4184     };
4185    
4186     $Element->{$HTML_NS}->{select} = {
4187 wakaba 1.56 %HTMLProseContentChecker, ## TODO: (optgroup|option)* [HTML4] + [WF2] ## TODO: SHOULD avoid empty and visible [WF2]
4188 wakaba 1.52 ## TODO: author should SELECTED at least one OPTION in non-MULTIPLE case [HTML4].
4189     ## TODO: more than one OPTION with SELECTED in non-MULTIPLE case is "error" [HTML4]
4190     status => FEATURE_WF2 | FEATURE_M12N10_REC,
4191 wakaba 1.56 is_root => 1, ## TODO: SHOULD NOT in application/xhtml+xml [WF2]
4192 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
4193 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4194 wakaba 1.56 autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
4195 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4196 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
4197     ## TODO: form [WF2]
4198 wakaba 1.52 multiple => $GetHTMLBooleanAttrChecker->('multiple'),
4199     name => sub {}, ## NOTE: CDATA [M12N]
4200 wakaba 1.56 oninvalid => $HTMLEventHandlerAttrChecker,
4201     ## TODO: pattern [WF2] ## TODO: |title| semantics
4202 wakaba 1.52 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4203     }, {
4204     %HTMLAttrStatus,
4205     %HTMLM12NCommonAttrStatus,
4206 wakaba 1.56 accesskey => FEATURE_WF2,
4207     autofocus => FEATURE_WF2,
4208     data => FEATURE_WF2,
4209 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
4210     dataformatas => FEATURE_HTML4_REC_RESERVED,
4211     datasrc => FEATURE_HTML4_REC_RESERVED,
4212 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
4213     form => FEATURE_WF2,
4214 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4215     multiple => FEATURE_M12N10_REC,
4216     name => FEATURE_M12N10_REC,
4217     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4218     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4219     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4220 wakaba 1.56 oninvalid => FEATURE_WF2,
4221     pattern => FEATURE_WF2,
4222 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
4223     sdapref => FEATURE_HTML20_RFC,
4224 wakaba 1.52 size => FEATURE_M12N10_REC,
4225     tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4226     }),
4227     ## TODO: Tests
4228     ## TODO: Tests for <nest/> in <select>
4229 wakaba 1.66 check_start => sub {
4230     my ($self, $item, $element_state) = @_;
4231    
4232     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
4233     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4234     },
4235 wakaba 1.52 };
4236 wakaba 1.1
4237 wakaba 1.52 $Element->{$HTML_NS}->{datalist} = {
4238 wakaba 1.56 %HTMLProseContentChecker, ## TODO: (transparent | option)*
4239     ## TODO: |option| child MUST be empty [WF2]
4240 wakaba 1.52 status => FEATURE_WF2,
4241 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
4242     data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
4243     }, {
4244 wakaba 1.52 %HTMLAttrStatus,
4245 wakaba 1.56 data => FEATURE_WF2,
4246 wakaba 1.52 }),
4247     ## TODO: Tests
4248     ## TODO: Tests for <nest/> in <datalist>
4249 wakaba 1.66 check_start => sub {
4250     my ($self, $item, $element_state) = @_;
4251    
4252     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
4253     },
4254 wakaba 1.52 };
4255 wakaba 1.49
4256 wakaba 1.52 $Element->{$HTML_NS}->{optgroup} = {
4257 wakaba 1.56 %HTMLProseContentChecker, ## TODO: (option|optgroup)* [HTML4] + [WF2] SHOULD avoid empty and visible [WF2]
4258 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
4259     check_attrs => $GetHTMLAttrsChecker->({
4260     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4261     label => sub {}, ## NOTE: Text [M12N] ## TODO: required
4262     }, {
4263     %HTMLAttrStatus,
4264     %HTMLM12NCommonAttrStatus,
4265 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
4266 wakaba 1.52 label => FEATURE_M12N10_REC,
4267     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4268     }),
4269     ## TODO: Tests
4270     ## TODO: Tests for <nest/> in <optgroup>
4271     };
4272    
4273     $Element->{$HTML_NS}->{option} = {
4274     %HTMLTextChecker,
4275     status => FEATURE_WF2 | FEATURE_M12N10_REC,
4276     check_attrs => $GetHTMLAttrsChecker->({
4277     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4278     label => sub {}, ## NOTE: Text [M12N]
4279     selected => $GetHTMLBooleanAttrChecker->('selected'),
4280     value => sub {}, ## NOTE: CDATA [M12N]
4281     }, {
4282     %HTMLAttrStatus,
4283     %HTMLM12NCommonAttrStatus,
4284 wakaba 1.56 disabled => FEATURE_WF2, FEATURE_M12N10_REC,
4285 wakaba 1.52 label => FEATURE_M12N10_REC,
4286     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4287 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
4288     sdapref => FEATURE_HTML20_RFC,
4289 wakaba 1.52 selected => FEATURE_M12N10_REC,
4290     value => FEATURE_M12N10_REC,
4291     }),
4292     ## TODO: Tests
4293     ## TODO: Tests for <nest/> in <option>
4294     };
4295 wakaba 1.49
4296 wakaba 1.52 $Element->{$HTML_NS}->{textarea} = {
4297     %HTMLTextChecker,
4298     status => FEATURE_WF2 | FEATURE_M12N10_REC,
4299     check_attrs => $GetHTMLAttrsChecker->({
4300 wakaba 1.56 accept => $HTMLIMTAttrChecker, ## TODO: MUST be a text-based type
4301 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4302 wakaba 1.56 autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
4303     cols => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## TODO: SHOULD if wrap=hard [WF2]
4304 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4305 wakaba 1.56 ## TODO: form [WF2]
4306     ## TODO: inputmode [WF2]
4307     maxlength => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4308 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
4309 wakaba 1.56 ## TODO: pattern [WF2] ## TODO: |title| special semantics
4310 wakaba 1.52 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
4311 wakaba 1.56 required => $GetHTMLBooleanAttrChecker->('required'),
4312     rows => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4313     oninvalid => $HTMLEventHandlerAttrChecker,
4314     wrap => $GetHTMLEnumeratedAttrChecker->({soft => 1, hard => 1}),
4315 wakaba 1.52 }, {
4316     %HTMLAttrStatus,
4317     %HTMLM12NCommonAttrStatus,
4318 wakaba 1.56 accept => FEATURE_WF2,
4319 wakaba 1.61 'accept-charset' => FEATURE_HTML2X_RFC,
4320 wakaba 1.52 accesskey => FEATURE_M12N10_REC,
4321 wakaba 1.56 autofocus => FEATURE_WF2,
4322 wakaba 1.52 cols => FEATURE_M12N10_REC,
4323     datafld => FEATURE_HTML4_REC_RESERVED,
4324 wakaba 1.49 dataformatas => FEATURE_HTML4_REC_RESERVED,
4325     datasrc => FEATURE_HTML4_REC_RESERVED,
4326 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
4327     form => FEATURE_WF2,
4328     inputmode => FEATURE_WF2 | FEATURE_XHTMLBASIC11_CR,
4329 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4330 wakaba 1.56 maxlength => FEATURE_WF2,
4331 wakaba 1.52 name => FEATURE_M12N10_REC,
4332     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4333     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4334     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4335 wakaba 1.56 oninvalid => FEATURE_WF2,
4336 wakaba 1.52 onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4337 wakaba 1.56 pattern => FEATURE_WF2,
4338     readonly => FEATURE_WF2 | FEATURE_M12N10_REC,
4339     required => FEATURE_WF2,
4340 wakaba 1.61 rows => FEATURE_M12N10_REC,
4341     sdaform => FEATURE_HTML20_RFC,
4342     sdapref => FEATURE_HTML20_RFC,
4343 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4344 wakaba 1.56 wrap => FEATURE_WF2,
4345 wakaba 1.52 }),
4346     ## TODO: Tests
4347     ## TODO: Tests for <nest/> in <textarea>
4348 wakaba 1.66 check_start => sub {
4349     my ($self, $item, $element_state) = @_;
4350    
4351     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
4352     },
4353 wakaba 1.52 };
4354 wakaba 1.49
4355 wakaba 1.52 $Element->{$HTML_NS}->{output} = {
4356 wakaba 1.56 %HTMLPhrasingContentChecker, ## Inline [WF2]
4357 wakaba 1.52 status => FEATURE_WF2,
4358 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
4359     ## TODO: for [WF2]
4360     ## TODO: form [WF2]
4361     ## TODO: name [WF2]
4362     ## onformchange[WF2]
4363     ## onforminput[WF2]
4364     }, {
4365 wakaba 1.52 %HTMLAttrStatus,
4366 wakaba 1.56 for => FEATURE_WF2,
4367     form => FEATURE_WF2,
4368     name => FEATURE_WF2,
4369     onchange => FEATURE_HTML5_DEFAULT | FEATURE_WF2,
4370     onformchange => FEATURE_WF2,
4371     onforminput => FEATURE_WF2,
4372 wakaba 1.52 }),
4373     ## TODO: Tests
4374     ## TODO: Tests for <nest/> in <output>
4375 wakaba 1.56 ## NOTE: "The output element should be used when ..." [WF2]
4376 wakaba 1.52 };
4377    
4378     ## TODO: repetition template
4379    
4380     $Element->{$HTML_NS}->{isindex} = {
4381     %HTMLEmptyChecker,
4382 wakaba 1.54 status => FEATURE_M12N10_REC_DEPRECATED |
4383     Whatpm::ContentChecker::FEATURE_DEPRECATED_SHOULD, ## [HTML4]
4384 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
4385     prompt => sub {}, ## NOTE: Text [M12N]
4386     }, {
4387     %HTMLAttrStatus,
4388     class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4389     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4390     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4391     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4392     prompt => FEATURE_M12N10_REC_DEPRECATED,
4393 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
4394 wakaba 1.52 style => FEATURE_XHTML10_REC,
4395     title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4396     }),
4397     ## TODO: Tests
4398     ## TODO: Tests for <nest/> in <isindex>
4399 wakaba 1.66 check_start => sub {
4400     my ($self, $item, $element_state) = @_;
4401    
4402     $element_state->{uri_info}->{action}->{type}->{action} = 1;
4403     },
4404 wakaba 1.52 };
4405 wakaba 1.49
4406 wakaba 1.1 $Element->{$HTML_NS}->{script} = {
4407 wakaba 1.40 %HTMLChecker,
4408 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4409 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4410 wakaba 1.70 ## TODO: HTML4 |charset|
4411     ## TODO: HTML4 |language|
4412 wakaba 1.1 src => $HTMLURIAttrChecker,
4413     defer => $GetHTMLBooleanAttrChecker->('defer'),
4414     async => $GetHTMLBooleanAttrChecker->('async'),
4415     type => $HTMLIMTAttrChecker,
4416 wakaba 1.49 }, {
4417     %HTMLAttrStatus,
4418     %HTMLM12NCommonAttrStatus,
4419 wakaba 1.50 async => FEATURE_HTML5_DEFAULT,
4420 wakaba 1.49 charset => FEATURE_M12N10_REC,
4421 wakaba 1.50 defer => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4422 wakaba 1.49 event => FEATURE_HTML4_REC_RESERVED,
4423     for => FEATURE_HTML4_REC_RESERVED,
4424 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4425 wakaba 1.49 language => FEATURE_M12N10_REC_DEPRECATED,
4426 wakaba 1.50 src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4427     type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4428 wakaba 1.9 }),
4429 wakaba 1.40 check_start => sub {
4430     my ($self, $item, $element_state) = @_;
4431 wakaba 1.1
4432 wakaba 1.40 if ($item->{node}->has_attribute_ns (undef, 'src')) {
4433     $element_state->{must_be_empty} = 1;
4434 wakaba 1.1 } else {
4435     ## NOTE: No content model conformance in HTML5 spec.
4436 wakaba 1.40 my $type = $item->{node}->get_attribute_ns (undef, 'type');
4437     my $language = $item->{node}->get_attribute_ns (undef, 'language');
4438 wakaba 1.1 if ((defined $type and $type eq '') or
4439     (defined $language and $language eq '')) {
4440     $type = 'text/javascript';
4441     } elsif (defined $type) {
4442     #
4443     } elsif (defined $language) {
4444     $type = 'text/' . $language;
4445     } else {
4446     $type = 'text/javascript';
4447     }
4448 wakaba 1.40 $element_state->{script_type} = $type; ## TODO: $type normalization
4449     }
4450 wakaba 1.66
4451     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
4452 wakaba 1.40 },
4453     check_child_element => sub {
4454     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4455     $child_is_transparent, $element_state) = @_;
4456     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4457     $self->{onerror}->(node => $child_el,
4458     type => 'element not allowed:minus',
4459     level => $self->{must_level});
4460     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4461     #
4462     } else {
4463     if ($element_state->{must_be_empty}) {
4464     $self->{onerror}->(node => $child_el,
4465     type => 'element not allowed');
4466     }
4467     }
4468     },
4469     check_child_text => sub {
4470     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4471     if ($has_significant and
4472     $element_state->{must_be_empty}) {
4473     $self->{onerror}->(node => $child_node,
4474     type => 'character not allowed');
4475     }
4476     },
4477     check_end => sub {
4478     my ($self, $item, $element_state) = @_;
4479     unless ($element_state->{must_be_empty}) {
4480     $self->{onerror}->(node => $item->{node}, level => 'unsupported',
4481     type => 'script:'.$element_state->{script_type});
4482     ## TODO: text/javascript support
4483    
4484     $HTMLChecker{check_end}->(@_);
4485 wakaba 1.1 }
4486     },
4487     };
4488 wakaba 1.25 ## ISSUE: Significant check and text child node
4489 wakaba 1.1
4490     ## NOTE: When script is disabled.
4491     $Element->{$HTML_NS}->{noscript} = {
4492 wakaba 1.40 %HTMLTransparentChecker,
4493 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4494     check_attrs => $GetHTMLAttrsChecker->({}, {
4495     %HTMLAttrStatus,
4496     %HTMLM12NCommonAttrStatus,
4497 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4498 wakaba 1.49 }),
4499 wakaba 1.40 check_start => sub {
4500     my ($self, $item, $element_state) = @_;
4501 wakaba 1.3
4502 wakaba 1.40 unless ($item->{node}->owner_document->manakai_is_html) {
4503     $self->{onerror}->(node => $item->{node}, type => 'in XML:noscript');
4504 wakaba 1.3 }
4505    
4506 wakaba 1.40 unless ($self->{flag}->{in_head}) {
4507     $self->_add_minus_elements ($element_state,
4508     {$HTML_NS => {noscript => 1}});
4509     }
4510 wakaba 1.3 },
4511 wakaba 1.40 check_child_element => sub {
4512     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4513     $child_is_transparent, $element_state) = @_;
4514     if ($self->{flag}->{in_head}) {
4515     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4516     $self->{onerror}->(node => $child_el,
4517     type => 'element not allowed:minus',
4518     level => $self->{must_level});
4519     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4520     #
4521     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'link') {
4522     #
4523     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') {
4524     if ($child_el->has_attribute_ns (undef, 'scoped')) {
4525     $self->{onerror}->(node => $child_el,
4526     type => 'element not allowed:head noscript',
4527     level => $self->{must_level});
4528     }
4529     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'meta') {
4530 wakaba 1.47 my $http_equiv_attr
4531     = $child_el->get_attribute_node_ns (undef, 'http-equiv');
4532     if ($http_equiv_attr) {
4533     ## TODO: case
4534     if (lc $http_equiv_attr->value eq 'content-type') {
4535 wakaba 1.40 $self->{onerror}->(node => $child_el,
4536 wakaba 1.34 type => 'element not allowed:head noscript',
4537     level => $self->{must_level});
4538 wakaba 1.47 } else {
4539     #
4540 wakaba 1.3 }
4541 wakaba 1.47 } else {
4542     $self->{onerror}->(node => $child_el,
4543     type => 'element not allowed:head noscript',
4544     level => $self->{must_level});
4545 wakaba 1.3 }
4546 wakaba 1.40 } else {
4547     $self->{onerror}->(node => $child_el,
4548     type => 'element not allowed:head noscript',
4549     level => $self->{must_level});
4550     }
4551     } else {
4552     $HTMLTransparentChecker{check_child_element}->(@_);
4553     }
4554     },
4555     check_child_text => sub {
4556     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4557     if ($self->{flag}->{in_head}) {
4558     if ($has_significant) {
4559     $self->{onerror}->(node => $child_node,
4560     type => 'character not allowed');
4561 wakaba 1.3 }
4562     } else {
4563 wakaba 1.40 $HTMLTransparentChecker{check_child_text}->(@_);
4564     }
4565     },
4566     check_end => sub {
4567     my ($self, $item, $element_state) = @_;
4568     $self->_remove_minus_elements ($element_state);
4569     if ($self->{flag}->{in_head}) {
4570     $HTMLChecker{check_end}->(@_);
4571     } else {
4572     $HTMLPhrasingContentChecker{check_end}->(@_);
4573 wakaba 1.3 }
4574 wakaba 1.1 },
4575     };
4576 wakaba 1.3 ## ISSUE: Scripting is disabled: <head><noscript><html a></noscript></head>
4577 wakaba 1.1
4578     $Element->{$HTML_NS}->{'event-source'} = {
4579 wakaba 1.40 %HTMLEmptyChecker,
4580 wakaba 1.48 status => FEATURE_HTML5_LC,
4581 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4582 wakaba 1.1 src => $HTMLURIAttrChecker,
4583 wakaba 1.50 }, {
4584     %HTMLAttrStatus,
4585     src => FEATURE_HTML5_LC,
4586 wakaba 1.1 }),
4587 wakaba 1.66 check_start => sub {
4588     my ($self, $item, $element_state) = @_;
4589    
4590     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
4591     },
4592 wakaba 1.1 };
4593    
4594     $Element->{$HTML_NS}->{details} = {
4595 wakaba 1.40 %HTMLProseContentChecker,
4596 wakaba 1.48 status => FEATURE_HTML5_WD,
4597 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4598 wakaba 1.1 open => $GetHTMLBooleanAttrChecker->('open'),
4599 wakaba 1.50 }, {
4600     %HTMLAttrStatus,
4601 wakaba 1.59 open => FEATURE_HTML5_WD,
4602 wakaba 1.1 }),
4603 wakaba 1.43 ## NOTE: legend, Prose
4604     check_child_element => sub {
4605     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4606     $child_is_transparent, $element_state) = @_;
4607     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4608     $self->{onerror}->(node => $child_el,
4609     type => 'element not allowed:minus',
4610     level => $self->{must_level});
4611     $element_state->{has_non_legend} = 1;
4612     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4613     #
4614     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') {
4615     if ($element_state->{has_non_legend}) {
4616     $self->{onerror}->(node => $child_el,
4617     type => 'element not allowed:details legend',
4618     level => $self->{must_level});
4619     }
4620     $element_state->{has_legend} = 1;
4621     $element_state->{has_non_legend} = 1;
4622     } else {
4623     $HTMLProseContentChecker{check_child_element}->(@_);
4624     $element_state->{has_non_legend} = 1 unless $child_is_transparent;
4625     ## ISSUE: |<details><object><legend>xx</legend></object>..</details>|
4626     ## is conforming?
4627     }
4628     },
4629     check_child_text => sub {
4630     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4631     if ($has_significant) {
4632     $element_state->{has_non_legend} = 1;
4633     }
4634     },
4635     check_end => sub {
4636     my ($self, $item, $element_state) = @_;
4637 wakaba 1.1
4638 wakaba 1.43 unless ($element_state->{has_legend}) {
4639     $self->{onerror}->(node => $item->{node},
4640     type => 'element missing:legend',
4641     level => $self->{must_level});
4642     }
4643    
4644     $HTMLProseContentChecker{check_end}->(@_);
4645     ## ISSUE: |<details><legend>aa</legend></details>| error?
4646 wakaba 1.1 },
4647     };
4648    
4649     $Element->{$HTML_NS}->{datagrid} = {
4650 wakaba 1.40 %HTMLProseContentChecker,
4651 wakaba 1.48 status => FEATURE_HTML5_WD,
4652 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4653 wakaba 1.1 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4654     multiple => $GetHTMLBooleanAttrChecker->('multiple'),
4655 wakaba 1.50 }, {
4656     %HTMLAttrStatus,
4657     disabled => FEATURE_HTML5_WD,
4658     multiple => FEATURE_HTML5_WD,
4659 wakaba 1.1 }),
4660 wakaba 1.40 check_start => sub {
4661     my ($self, $item, $element_state) = @_;
4662 wakaba 1.1
4663 wakaba 1.40 $self->_add_minus_elements ($element_state,
4664     {$HTML_NS => {a => 1, datagrid => 1}});
4665     $element_state->{phase} = 'any';
4666     },
4667     ## Prose -(text* table Prose*) | table | select | datalist | Empty
4668     check_child_element => sub {
4669     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4670     $child_is_transparent, $element_state) = @_;
4671     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4672     $self->{onerror}->(node => $child_el,
4673     type => 'element not allowed:minus',
4674     level => $self->{must_level});
4675     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4676     #
4677     } elsif ($element_state->{phase} eq 'prose') {
4678     if ($HTMLProseContent->{$child_nsuri}->{$child_ln}) {
4679 wakaba 1.44 if (not $element_state->{has_element} and
4680 wakaba 1.40 $child_nsuri eq $HTML_NS and
4681     $child_ln eq 'table') {
4682     $self->{onerror}->(node => $child_el,
4683     type => 'element not allowed');
4684     } else {
4685 wakaba 1.8 #
4686 wakaba 1.1 }
4687 wakaba 1.40 } else {
4688     $self->{onerror}->(node => $child_el,
4689     type => 'element not allowed');
4690     }
4691 wakaba 1.43 $element_state->{has_element} = 1;
4692 wakaba 1.40 } elsif ($element_state->{phase} eq 'any') {
4693     if ($child_nsuri eq $HTML_NS and
4694     {table => 1, select => 1, datalist => 1}->{$child_ln}) {
4695     $element_state->{phase} = 'none';
4696     } elsif ($HTMLProseContent->{$child_nsuri}->{$child_ln}) {
4697     $element_state->{has_element} = 1;
4698     $element_state->{phase} = 'prose';
4699 wakaba 1.43 ## TODO: transparent?
4700 wakaba 1.40 } else {
4701     $self->{onerror}->(node => $child_el,
4702     type => 'element not allowed');
4703     }
4704     } elsif ($element_state->{phase} eq 'none') {
4705     $self->{onerror}->(node => $child_el,
4706     type => 'element not allowed');
4707     } else {
4708     die "check_child_element: Bad |datagrid| phase: $element_state->{phase}";
4709     }
4710     },
4711     check_child_text => sub {
4712     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4713     if ($has_significant) {
4714     if ($element_state->{phase} eq 'prose') {
4715     #
4716     } elsif ($element_state->{phase} eq 'any') {
4717     $element_state->{phase} = 'prose';
4718     } else {
4719     $self->{onerror}->(node => $child_node,
4720     type => 'character not allowed');
4721 wakaba 1.1 }
4722     }
4723 wakaba 1.40 },
4724     check_end => sub {
4725     my ($self, $item, $element_state) = @_;
4726     $self->_remove_minus_elements ($element_state);
4727 wakaba 1.1
4728 wakaba 1.40 if ($element_state->{phase} eq 'none') {
4729     $HTMLChecker{check_end}->(@_);
4730     } else {
4731     $HTMLPhrasingContentChecker{check_end}->(@_);
4732     }
4733     },
4734 wakaba 1.29 ## ISSUE: "xxx<table/>" is disallowed; "<select/>aaa" and "<datalist/>aa"
4735     ## are not disallowed (assuming that form control contents are also
4736     ## prose content).
4737 wakaba 1.1 };
4738    
4739     $Element->{$HTML_NS}->{command} = {
4740 wakaba 1.40 %HTMLEmptyChecker,
4741 wakaba 1.48 status => FEATURE_HTML5_WD,
4742 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4743 wakaba 1.1 checked => $GetHTMLBooleanAttrChecker->('checked'),
4744     default => $GetHTMLBooleanAttrChecker->('default'),
4745     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4746     hidden => $GetHTMLBooleanAttrChecker->('hidden'),
4747     icon => $HTMLURIAttrChecker,
4748     label => sub { }, ## NOTE: No conformance creteria
4749     radiogroup => sub { }, ## NOTE: No conformance creteria
4750     type => sub {
4751     my ($self, $attr) = @_;
4752     my $value = $attr->value;
4753     unless ({command => 1, checkbox => 1, radio => 1}->{$value}) {
4754     $self->{onerror}->(node => $attr, type => 'attribute value not allowed');
4755     }
4756     },
4757 wakaba 1.50 }, {
4758     %HTMLAttrStatus,
4759     checked => FEATURE_HTML5_WD,
4760     default => FEATURE_HTML5_WD,
4761     disabled => FEATURE_HTML5_WD,
4762     hidden => FEATURE_HTML5_WD,
4763     icon => FEATURE_HTML5_WD,
4764     label => FEATURE_HTML5_WD,
4765     radiogroup => FEATURE_HTML5_WD,
4766     type => FEATURE_HTML5_WD,
4767 wakaba 1.1 }),
4768 wakaba 1.66 check_start => sub {
4769     my ($self, $item, $element_state) = @_;
4770    
4771     $element_state->{uri_info}->{icon}->{type}->{embedded} = 1;
4772     },
4773 wakaba 1.1 };
4774    
4775     $Element->{$HTML_NS}->{menu} = {
4776 wakaba 1.40 %HTMLPhrasingContentChecker,
4777 wakaba 1.54 #status => FEATURE_M12N10_REC_DEPRECATED | FEATURE_HTML5_WD,
4778     status => FEATURE_M12N10_REC | FEATURE_HTML5_WD,
4779     ## NOTE: We don't want any |menu| element warned as deprecated.
4780 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4781 wakaba 1.1 autosubmit => $GetHTMLBooleanAttrChecker->('autosubmit'),
4782 wakaba 1.68 compact => $GetHTMLBooleanAttrChecker->('compact'),
4783 wakaba 1.1 id => sub {
4784     ## NOTE: same as global |id=""|, with |$self->{menu}| registeration
4785     my ($self, $attr) = @_;
4786     my $value = $attr->value;
4787     if (length $value > 0) {
4788     if ($self->{id}->{$value}) {
4789     $self->{onerror}->(node => $attr, type => 'duplicate ID');
4790     push @{$self->{id}->{$value}}, $attr;
4791     } else {
4792     $self->{id}->{$value} = [$attr];
4793     }
4794     } else {
4795     ## NOTE: MUST contain at least one character
4796     $self->{onerror}->(node => $attr, type => 'empty attribute value');
4797     }
4798     if ($value =~ /[\x09-\x0D\x20]/) {
4799     $self->{onerror}->(node => $attr, type => 'space in ID');
4800     }
4801     $self->{menu}->{$value} ||= $attr;
4802     ## ISSUE: <menu id=""><p contextmenu=""> match?
4803     },
4804     label => sub { }, ## NOTE: No conformance creteria
4805     type => $GetHTMLEnumeratedAttrChecker->({context => 1, toolbar => 1}),
4806 wakaba 1.49 }, {
4807     %HTMLAttrStatus,
4808     %HTMLM12NCommonAttrStatus,
4809 wakaba 1.61 align => FEATURE_HTML2X_RFC,
4810 wakaba 1.50 autosubmit => FEATURE_HTML5_WD,
4811 wakaba 1.49 compat => FEATURE_M12N10_REC_DEPRECATED,
4812 wakaba 1.50 label => FEATURE_HTML5_WD,
4813     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4814 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
4815     sdapref => FEATURE_HTML20_RFC,
4816 wakaba 1.50 type => FEATURE_HTML5_WD,
4817 wakaba 1.1 }),
4818 wakaba 1.40 check_start => sub {
4819     my ($self, $item, $element_state) = @_;
4820     $element_state->{phase} = 'li or phrasing';
4821     $element_state->{in_menu_original} = $self->{flag}->{in_menu};
4822     $self->{flag}->{in_menu} = 1;
4823     },
4824     check_child_element => sub {
4825     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4826     $child_is_transparent, $element_state) = @_;
4827     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4828     $self->{onerror}->(node => $child_el,
4829     type => 'element not allowed:minus',
4830     level => $self->{must_level});
4831     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4832     #
4833     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'li') {
4834     if ($element_state->{phase} eq 'li') {
4835     #
4836     } elsif ($element_state->{phase} eq 'li or phrasing') {
4837     $element_state->{phase} = 'li';
4838     } else {
4839     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4840     }
4841     } elsif ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
4842     if ($element_state->{phase} eq 'phrasing') {
4843     #
4844     } elsif ($element_state->{phase} eq 'li or phrasing') {
4845     $element_state->{phase} = 'phrasing';
4846     } else {
4847     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4848     }
4849     } else {
4850     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4851     }
4852     },
4853     check_child_text => sub {
4854     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4855     if ($has_significant) {
4856     if ($element_state->{phase} eq 'phrasing') {
4857     #
4858     } elsif ($element_state->{phase} eq 'li or phrasing') {
4859     $element_state->{phase} = 'phrasing';
4860     } else {
4861     $self->{onerror}->(node => $child_node,
4862     type => 'character not allowed');
4863 wakaba 1.1 }
4864     }
4865 wakaba 1.40 },
4866     check_end => sub {
4867     my ($self, $item, $element_state) = @_;
4868     delete $self->{flag}->{in_menu} unless $element_state->{in_menu_original};
4869    
4870     if ($element_state->{phase} eq 'li') {
4871     $HTMLChecker{check_end}->(@_);
4872     } else { # 'phrasing' or 'li or phrasing'
4873     $HTMLPhrasingContentChecker{check_end}->(@_);
4874 wakaba 1.1 }
4875     },
4876 wakaba 1.8 };
4877    
4878     $Element->{$HTML_NS}->{datatemplate} = {
4879 wakaba 1.40 %HTMLChecker,
4880 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
4881 wakaba 1.40 check_child_element => sub {
4882     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4883     $child_is_transparent, $element_state) = @_;
4884     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4885     $self->{onerror}->(node => $child_el,
4886     type => 'element not allowed:minus',
4887     level => $self->{must_level});
4888     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4889     #
4890     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'rule') {
4891     #
4892     } else {
4893     $self->{onerror}->(node => $child_el,
4894     type => 'element not allowed:datatemplate');
4895     }
4896     },
4897     check_child_text => sub {
4898     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4899     if ($has_significant) {
4900     $self->{onerror}->(node => $child_node, type => 'character not allowed');
4901 wakaba 1.8 }
4902     },
4903     is_xml_root => 1,
4904     };
4905    
4906     $Element->{$HTML_NS}->{rule} = {
4907 wakaba 1.40 %HTMLChecker,
4908 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
4909 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4910 wakaba 1.23 condition => $HTMLSelectorsAttrChecker,
4911 wakaba 1.18 mode => $HTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker,
4912 wakaba 1.50 }, {
4913     %HTMLAttrStatus,
4914     condition => FEATURE_HTML5_AT_RISK,
4915     mode => FEATURE_HTML5_AT_RISK,
4916 wakaba 1.8 }),
4917 wakaba 1.40 check_start => sub {
4918     my ($self, $item, $element_state) = @_;
4919     $self->_add_plus_elements ($element_state, {$HTML_NS => {nest => 1}});
4920     },
4921     check_child_element => sub { },
4922     check_child_text => sub { },
4923     check_end => sub {
4924     my ($self, $item, $element_state) = @_;
4925     $self->_remove_plus_elements ($element_state);
4926     $HTMLChecker{check_end}->(@_);
4927 wakaba 1.8 },
4928     ## NOTE: "MAY be anything that, when the parent |datatemplate|
4929     ## is applied to some conforming data, results in a conforming DOM tree.":
4930     ## We don't check against this.
4931     };
4932    
4933     $Element->{$HTML_NS}->{nest} = {
4934 wakaba 1.40 %HTMLEmptyChecker,
4935 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
4936 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4937 wakaba 1.23 filter => $HTMLSelectorsAttrChecker,
4938     mode => sub {
4939     my ($self, $attr) = @_;
4940     my $value = $attr->value;
4941     if ($value !~ /\A[^\x09-\x0D\x20]+\z/) {
4942     $self->{onerror}->(node => $attr, type => 'mode:syntax error');
4943     }
4944     },
4945 wakaba 1.50 }, {
4946     %HTMLAttrStatus,
4947     filter => FEATURE_HTML5_AT_RISK,
4948     mode => FEATURE_HTML5_AT_RISK,
4949 wakaba 1.8 }),
4950 wakaba 1.1 };
4951    
4952     $Element->{$HTML_NS}->{legend} = {
4953 wakaba 1.40 %HTMLPhrasingContentChecker,
4954 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4955 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
4956 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4957 wakaba 1.52 # align => $GetHTMLEnumeratedAttrChecker->({
4958     # top => 1, bottom => 1, left => 1, right => 1,
4959     # }),
4960     }, {
4961 wakaba 1.49 %HTMLAttrStatus,
4962     %HTMLM12NCommonAttrStatus,
4963     accesskey => FEATURE_M12N10_REC,
4964     align => FEATURE_M12N10_REC_DEPRECATED,
4965 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4966 wakaba 1.49 }),
4967 wakaba 1.1 };
4968    
4969     $Element->{$HTML_NS}->{div} = {
4970 wakaba 1.40 %HTMLProseContentChecker,
4971 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4972 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
4973     align => $GetHTMLEnumeratedAttrChecker->({
4974     left => 1, center => 1, right => 1, justify => 1,
4975     }),
4976     }, {
4977 wakaba 1.49 %HTMLAttrStatus,
4978     %HTMLM12NCommonAttrStatus,
4979     align => FEATURE_M12N10_REC_DEPRECATED,
4980     datafld => FEATURE_HTML4_REC_RESERVED,
4981     dataformatas => FEATURE_HTML4_REC_RESERVED,
4982     datasrc => FEATURE_HTML4_REC_RESERVED,
4983 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4984 wakaba 1.49 }),
4985 wakaba 1.66 check_start => sub {
4986     my ($self, $item, $element_state) = @_;
4987    
4988     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4989     },
4990 wakaba 1.1 };
4991    
4992 wakaba 1.64 $Element->{$HTML_NS}->{center} = {
4993     %HTMLProseContentChecker,
4994     status => FEATURE_M12N10_REC_DEPRECATED,
4995     check_attrs => $GetHTMLAttrsChecker->({}, {
4996     %HTMLAttrStatus,
4997     %HTMLM12NCommonAttrStatus,
4998     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4999     }),
5000     };
5001    
5002 wakaba 1.1 $Element->{$HTML_NS}->{font} = {
5003 wakaba 1.40 %HTMLTransparentChecker,
5004 wakaba 1.50 status => FEATURE_HTML5_AT_RISK | FEATURE_M12N10_REC_DEPRECATED,
5005 wakaba 1.70 check_attrs => $GetHTMLAttrsChecker->({
5006     ## TODO: HTML4 |size|, |color|, |face|
5007     ## TODO: HTML5 |style|
5008 wakaba 1.49 }, {
5009     %HTMLAttrStatus,
5010 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5011 wakaba 1.49 color => FEATURE_M12N10_REC_DEPRECATED,
5012 wakaba 1.50 dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5013 wakaba 1.49 face => FEATURE_M12N10_REC_DEPRECATED,
5014 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5015     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5016 wakaba 1.49 size => FEATURE_M12N10_REC_DEPRECATED,
5017 wakaba 1.50 style => FEATURE_HTML5_AT_RISK | FEATURE_XHTML10_REC,
5018     title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5019 wakaba 1.49 }),
5020 wakaba 1.1 };
5021 wakaba 1.49
5022 wakaba 1.64 $Element->{$HTML_NS}->{basefont} = {
5023     %HTMLEmptyChecker,
5024     status => FEATURE_M12N10_REC_DEPRECATED,
5025     check_attrs => $GetHTMLAttrsChecker->({
5026     ## TODO: color, face, size
5027     }, {
5028     %HTMLAttrStatus,
5029     color => FEATURE_M12N10_REC_DEPRECATED,
5030     face => FEATURE_M12N10_REC_DEPRECATED,
5031     #id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
5032     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5033     size => FEATURE_M12N10_REC_DEPRECATED,
5034     }),
5035     };
5036    
5037 wakaba 1.49 ## TODO: frameset FEATURE_M12N10_REC
5038     ## class title id cols rows onload onunload style(x10)
5039     ## frame frameborder longdesc marginheight marginwidth noresize scrolling src name(deprecated) class,id,title,style(x10)
5040     ## noframes Common, lang(xhtml10)
5041    
5042     ## TODO: CR: ruby rb rt rp rbc rtc @rbspan
5043 wakaba 1.56
5044 wakaba 1.61 ## TODO: xmp, listing, plaintext FEATURE_HTML32_REC_OBSOLETE
5045     ## TODO: ^^^ lang, dir, id, class [HTML 2.x] sdaform [HTML 2.0]
5046     ## xmp, listing sdapref[HTML2,0]
5047    
5048 wakaba 1.56 =pod
5049    
5050     WF2: Documents MUST comply to [CHARMOD].
5051     WF2: Vencor extensions MUST NOT be used.
5052    
5053 wakaba 1.61 HTML 2.0 nextid @n
5054    
5055     RFC 2659: CERTS CRYPTOPTS
5056    
5057     ISO-HTML: pre-html, divN
5058    
5059 wakaba 1.56 =cut
5060 wakaba 1.61
5061     ## NOTE: Where RFC 2659 allows additional attributes is unclear.
5062     ## We added them only to |a|. |link| and |form| might also allow them
5063     ## in theory.
5064 wakaba 1.1
5065     $Whatpm::ContentChecker::Namespace->{$HTML_NS}->{loaded} = 1;
5066    
5067     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24