/[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.80 - (hide annotations) (download)
Sat May 3 09:06:57 2008 UTC (16 years, 6 months ago) by wakaba
Branch: MAIN
Changes since 1.79: +43 -3 lines
++ whatpm/Whatpm/ContentChecker/ChangeLog	3 May 2008 09:06:48 -0000
	* HTML.pm: Support for |repeat=""|, |repeat-template=""|,
	|repeat-min=""|, and |repeat-max=""| attributes.
	Support for |button| |type| values |add|, |remove|, |move-up|,
	and |move-down|.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24