/[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.61 - (hide annotations) (download)
Sun Mar 2 12:57:53 2008 UTC (16 years, 8 months ago) by wakaba
Branch: MAIN
Changes since 1.60: +132 -9 lines
++ whatpm/Whatpm/ContentChecker/ChangeLog	2 Mar 2008 12:56:09 -0000
	* HTML.pm: Status information from old specifications
	are incorporated.

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

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24