/[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.65 - (hide annotations) (download)
Mon Mar 17 13:32:31 2008 UTC (16 years, 7 months ago) by wakaba
Branch: MAIN
Changes since 1.64: +3 -0 lines
++ whatpm/Whatpm/ContentChecker/ChangeLog	17 Mar 2008 13:32:29 -0000
2008-03-17  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: Some attribute level definitions were missing.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24