/[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.91 - (hide annotations) (download)
Fri May 16 13:56:16 2008 UTC (17 years, 2 months ago) by wakaba
Branch: MAIN
Changes since 1.90: +95 -59 lines
++ whatpm/t/ChangeLog	16 May 2008 13:45:01 -0000
	* content-model-2.dat: Test data for charset="" attribute
	are added.

2008-05-16  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/ChangeLog	16 May 2008 11:18:17 -0000
2008-05-16  Wakaba  <wakaba@suika.fam.cx>

	* CacheManifest.pm (_parse): Drop fragment identifiers from
	URIs in fallback section (HTML5 revision 1596).

++ whatpm/Whatpm/ContentChecker/ChangeLog	16 May 2008 13:45:47 -0000
	* HTML.pm: Fact out generic charset name checking code
	to $HTMLCharsetChecker.  Support for charset="" attributes
	on <a>, <link>, and <script> elements.

2008-05-16  Wakaba  <wakaba@suika.fam.cx>

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24