/[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.78 - (hide annotations) (download)
Tue Apr 29 12:57:22 2008 UTC (16 years, 6 months ago) by wakaba
Branch: MAIN
Changes since 1.77: +16 -7 lines
++ whatpm/t/ChangeLog	29 Apr 2008 12:39:24 -0000
	* content-model-1.dat: Test data for <font> context are added.

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	29 Apr 2008 12:40:15 -0000
	* HTML.pm: <font> dropped and style="" is now global (HTML5 revision
	1511).

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24