/[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.76 - (hide annotations) (download)
Tue Apr 29 10:25:08 2008 UTC (17 years, 2 months ago) by wakaba
Branch: MAIN
Changes since 1.75: +30 -6 lines
++ whatpm/t/ChangeLog	29 Apr 2008 10:24:55 -0000
2008-04-29  Wakaba  <wakaba@suika.fam.cx>

	* content-model-1.dat, content-model-2.dat: Test results
	related to browsing context name or keyword are revised
	and added.

++ whatpm/Whatpm/ChangeLog	29 Apr 2008 08:17:14 -0000
	* CacheManifest.pm (_parse): New same origin definition (HTML5 revision
	1500) is implemented (except for IDNA part and URI-scheme-specific
++ whatpm/Whatpm/ContentChecker/ChangeLog	29 Apr 2008 10:23:48 -0000
2008-04-29  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm ($HTMLBrowsingContextNameAttrChecker): New checker.
	($HTMLTargetAttrChecker): |_blank| is now allowed and
	an empty string is no longer allowed (HTML5 revision 1470).
	(object/@name, iframe/@name): Added (HTML5 revision 1470).

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24