/[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.64 - (hide annotations) (download)
Sun Mar 9 13:56:09 2008 UTC (16 years, 8 months ago) by wakaba
Branch: MAIN
Changes since 1.63: +40 -5 lines
++ whatpm/t/ChangeLog	9 Mar 2008 13:55:01 -0000
	* content-model-1.dat: Test data for |center|, |dir|, and |basefont|
	are added.

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	9 Mar 2008 13:55:42 -0000
	* HTML.pm: |dir|, |center|, and |basefont| are implemented.

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

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24