/[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.73 - (hide annotations) (download)
Sat Apr 12 07:38:36 2008 UTC (17 years, 3 months ago) by wakaba
Branch: MAIN
Changes since 1.72: +103 -72 lines
++ whatpm/t/ChangeLog	12 Apr 2008 07:18:37 -0000
2008-04-12  Wakaba  <wakaba@suika.fam.cx>

	* content-model-6.dat: New test data file.  Test data for
	HTML custom data attributes (HTML5 revision 1399) are added.

	* ContentChecker.t: New test file |content-model-6.dat| is added.

++ whatpm/Whatpm/ContentChecker/ChangeLog	12 Apr 2008 07:19:00 -0000
2008-04-12  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: Support for HTML custom data attributes (HTML5
	revision 1399).

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24