/[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.75 - (hide annotations) (download)
Sun Apr 13 06:44:27 2008 UTC (17 years, 3 months ago) by wakaba
Branch: MAIN
Changes since 1.74: +5 -0 lines
++ whatpm/t/ChangeLog	13 Apr 2008 06:43:36 -0000
	* HTML-tokenizer.t, HTML-tree.t, Makefile: Support for html5lib
	new test files.

2008-04-13  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/ChangeLog	13 Apr 2008 06:44:17 -0000
	* mkentitylist.pl: Support for new HTML5 entity table format (the
	definition for |AElig;| was missing).

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24