/[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.97 - (hide annotations) (download)
Sun Jun 1 05:35:53 2008 UTC (18 years, 4 months ago) by wakaba
Branch: MAIN
Changes since 1.96: +6 -6 lines
++ whatpm/t/ChangeLog	1 Jun 2008 05:29:24 -0000
2008-06-01  Wakaba  <wakaba@suika.fam.cx>

	* content-model-6.dat: "data-" attribute is no longer allowed (HTML5
	revision 1709).

	* content-model-2.dat: Add tests for rel=contact (cf. HTML5
	revision 1711).

++ whatpm/Whatpm/ChangeLog	1 Jun 2008 05:22:21 -0000
2008-06-01  Wakaba  <wakaba@suika.fam.cx>

	* mklinktypelist.pl: rel=contact is no longer part of the HTML5
	spec (commented out). (HTML5 revision 1711).

++ whatpm/Whatpm/ContentChecker/ChangeLog	1 Jun 2008 05:29:43 -0000
2008-06-01  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: Make data="" invalid (HTML5 revision 1709).

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24