/[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.94 - (hide annotations) (download)
Sun May 25 06:03:57 2008 UTC (17 years, 1 month ago) by wakaba
Branch: MAIN
Changes since 1.93: +3 -3 lines
++ whatpm/t/ChangeLog	25 May 2008 05:59:46 -0000
2008-05-25  Wakaba  <wakaba@suika.fam.cx>

	* tree-test-1.dat: Test data on <isindex/> are added (HTML5
	revision 1697).

++ whatpm/Whatpm/ChangeLog	25 May 2008 06:00:14 -0000
2008-05-25  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm.src: Don't raise parse error for <isindex/> (HTML5
	revision 1697).

++ whatpm/Whatpm/ContentChecker/ChangeLog	25 May 2008 02:05:07 -0000
2008-05-25  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: The |param| element is in LC phase now.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24