/[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.62 - (hide annotations) (download)
Sun Mar 9 11:08:11 2008 UTC (16 years, 8 months ago) by wakaba
Branch: MAIN
Changes since 1.61: +81 -70 lines
++ whatpm/Whatpm/ContentChecker/ChangeLog	9 Mar 2008 10:54:53 -0000
2008-03-09  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: Known but not-implemented-yet attributes were not
	warned.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24