/[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.95 - (hide annotations) (download)
Sun May 25 08:13:07 2008 UTC (17 years, 2 months ago) by wakaba
Branch: MAIN
Changes since 1.94: +27 -9 lines
++ whatpm/t/ChangeLog	25 May 2008 07:59:59 -0000
	* content-model-1.dat: Test data on <datagrid> content model
	are added (cf. HTML5 revision 1566).  Empty |datagrid| elements
	are no longer in error.

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	25 May 2008 07:59:38 -0000
	* HTML.pm: |select| or |datalist| as a first child of |datagrid|
	is now disallowed (HTML5 revision 1566).  |datagrid| elements
	may be empty.

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

1 wakaba 1.1 package Whatpm::ContentChecker;
2     use strict;
3     require Whatpm::ContentChecker;
4    
5     my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
6    
7 wakaba 1.58 sub FEATURE_HTML5_ROLE () {
8     Whatpm::ContentChecker::FEATURE_STATUS_WD
9     ## TODO: svg:*/@role
10     }
11    
12 wakaba 1.89 sub FEATURE_HTML5_COMPLETE () {
13     Whatpm::ContentChecker::FEATURE_STATUS_REC |
14     Whatpm::ContentChecker::FEATURE_ALLOWED
15     }
16    
17 wakaba 1.54 sub FEATURE_HTML5_LC () {
18     Whatpm::ContentChecker::FEATURE_STATUS_LC |
19     Whatpm::ContentChecker::FEATURE_ALLOWED
20     }
21     sub FEATURE_HTML5_AT_RISK () {
22     Whatpm::ContentChecker::FEATURE_STATUS_WD |
23     Whatpm::ContentChecker::FEATURE_ALLOWED
24     }
25     sub FEATURE_HTML5_WD () {
26     Whatpm::ContentChecker::FEATURE_STATUS_WD |
27     Whatpm::ContentChecker::FEATURE_ALLOWED
28     }
29     sub FEATURE_HTML5_FD () {
30     Whatpm::ContentChecker::FEATURE_STATUS_WD |
31     Whatpm::ContentChecker::FEATURE_ALLOWED
32     }
33     sub FEATURE_HTML5_DEFAULT () {
34     Whatpm::ContentChecker::FEATURE_STATUS_WD |
35     Whatpm::ContentChecker::FEATURE_ALLOWED
36 wakaba 1.49 }
37 wakaba 1.54 sub FEATURE_HTML5_DROPPED () {
38     ## NOTE: Was part of HTML5, but was dropped.
39 wakaba 1.49 Whatpm::ContentChecker::FEATURE_STATUS_WD
40     }
41 wakaba 1.54 sub FEATURE_WF2 () {
42     Whatpm::ContentChecker::FEATURE_STATUS_LC |
43     Whatpm::ContentChecker::FEATURE_ALLOWED
44     }
45 wakaba 1.56 sub FEATURE_WF2_DEPRECATED () {
46     Whatpm::ContentChecker::FEATURE_STATUS_LC
47     ## NOTE: MUST NOT be used.
48     }
49 wakaba 1.49
50 wakaba 1.61 ## NOTE: Metainformation Attributes Module by W3C XHTML2 WG.
51     sub FEATURE_RDFA_LC () {
52     Whatpm::ContentChecker::FEATURE_STATUS_LC
53     }
54 wakaba 1.58
55 wakaba 1.82 sub FEATURE_RDFA_ED () {
56     Whatpm::ContentChecker::FEATURE_STATUS_WD
57     }
58    
59 wakaba 1.58 ## NOTE: XHTML Role LCWD has almost no information on how the |role|
60     ## attribute can be used- the only requirements for that matter is:
61     ## "the attribute MUST be referenced using its namespace-qualified form" (and
62     ## this is a host language conformance!).
63 wakaba 1.82 sub FEATURE_ROLE_LC () {
64     Whatpm::ContentChecker::FEATURE_STATUS_LC
65     }
66    
67     sub FEATURE_XHTML2_ED () {
68     Whatpm::ContentChecker::FEATURE_STATUS_WD
69     }
70 wakaba 1.58
71 wakaba 1.55 sub FEATURE_XHTMLBASIC11_CR () {
72     ## NOTE: Only additions to M12N10_REC are marked.
73     Whatpm::ContentChecker::FEATURE_STATUS_CR
74     }
75     sub FEATURE_XHTMLBASIC11_CR_DEPRECATED () {
76     Whatpm::ContentChecker::FEATURE_STATUS_CR |
77     Whatpm::ContentChecker::FEATURE_DEPRECATED_INFO
78     }
79    
80 wakaba 1.82 sub FEATURE_M12N11_LC () {
81     Whatpm::ContentChecker::FEATURE_STATUS_LC
82     }
83    
84 wakaba 1.49 ## NOTE: M12N10 status is based on its abstract module definition.
85     ## It contains a number of problems. (However, again, it's a REC!)
86 wakaba 1.54 sub FEATURE_M12N10_REC () {
87     ## NOTE: Oh, XHTML m12n 1.0 passed the CR phase! W3C Process suck!
88     Whatpm::ContentChecker::FEATURE_STATUS_REC
89     }
90     sub FEATURE_M12N10_REC_DEPRECATED () {
91     Whatpm::ContentChecker::FEATURE_STATUS_REC |
92     Whatpm::ContentChecker::FEATURE_DEPRECATED_INFO
93     }
94 wakaba 1.58 ## NOTE: XHTML M12N 1.1 is a LC at the time of writing and no
95     ## addition from 1.0.
96 wakaba 1.49
97     ## NOTE: XHTML10 status is based on its transitional and frameset DTDs
98     ## (second edition). Only missing attributes from M12N10 abstract
99     ## definition are added.
100 wakaba 1.54 sub FEATURE_XHTML10_REC () {
101     Whatpm::ContentChecker::FEATURE_STATUS_CR
102     }
103    
104 wakaba 1.61 ## NOTE: Diff from HTML4.
105     sub FEATURE_ISOHTML_PREPARATION () { ## Informative documentation
106     Whatpm::ContentChecker::FEATURE_STATUS_CR
107     }
108 wakaba 1.58
109 wakaba 1.49 ## NOTE: HTML4 status is based on its transitional and frameset DTDs (HTML
110     ## 4.01). Only missing attributes from XHTML10 are added.
111 wakaba 1.54 sub FEATURE_HTML4_REC_RESERVED () {
112     Whatpm::ContentChecker::FEATURE_STATUS_WD
113     }
114    
115     ## TODO: According to HTML4 definition, authors SHOULD use style sheets
116     ## rather than presentational attributes (deprecated or not deprecated).
117 wakaba 1.48
118 wakaba 1.61 ## NOTE: Diff from HTML4.
119     sub FEATURE_HTML32_REC_OBSOLETE () {
120     Whatpm::ContentChecker::FEATURE_STATUS_CR |
121     Whatpm::ContentChecker::FEATURE_DEPRECATED_SHOULD
122     ## NOTE: Lowercase normative "should".
123     }
124    
125     sub FEATURE_RFC2659 () { ## Experimental RFC
126     Whatpm::ContentChecker::FEATURE_STATUS_CR
127     }
128    
129     ## NOTE: HTML 2.x - diff from HTML 2.0 and not in newer versions.
130     sub FEATURE_HTML2X_RFC () { ## Proposed Standard, obsolete
131     Whatpm::ContentChecker::FEATURE_STATUS_CR
132     }
133    
134     ## NOTE: Diff from HTML 2.0.
135     sub FEATURE_RFC1942 () { ## Experimental RFC, obsolete
136     Whatpm::ContentChecker::FEATURE_STATUS_CR
137     }
138    
139     ## NOTE: Diff from HTML 3.2.
140     sub FEATURE_HTML20_RFC () { ## Proposed Standard, obsolete
141     Whatpm::ContentChecker::FEATURE_STATUS_CR
142     }
143 wakaba 1.58
144 wakaba 1.29 ## December 2007 HTML5 Classification
145    
146     my $HTMLMetadataContent = {
147     $HTML_NS => {
148     title => 1, base => 1, link => 1, style => 1, script => 1, noscript => 1,
149     'event-source' => 1, command => 1, datatemplate => 1,
150     ## NOTE: A |meta| with no |name| element is not allowed as
151     ## a metadata content other than |head| element.
152     meta => 1,
153 wakaba 1.56 ## NOTE: Only when empty [WF2]
154     form => 1,
155 wakaba 1.29 },
156     ## NOTE: RDF is mentioned in the HTML5 spec.
157     ## TODO: Other RDF elements?
158     q<http://www.w3.org/1999/02/22-rdf-syntax-ns#> => {RDF => 1},
159     };
160    
161 wakaba 1.72 my $HTMLFlowContent = {
162 wakaba 1.29 $HTML_NS => {
163     section => 1, nav => 1, article => 1, blockquote => 1, aside => 1,
164     h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1, header => 1,
165     footer => 1, address => 1, p => 1, hr => 1, dialog => 1, pre => 1,
166     ol => 1, ul => 1, dl => 1, figure => 1, map => 1, table => 1,
167 wakaba 1.72 details => 1, ## ISSUE: "Flow element" in spec.
168     datagrid => 1, ## ISSUE: "Flow element" in spec.
169 wakaba 1.29 datatemplate => 1,
170     div => 1, ## ISSUE: No category in spec.
171     ## NOTE: |style| is only allowed if |scoped| attribute is specified.
172     ## Additionally, it must be before any other element or
173     ## non-inter-element-whitespace text node.
174     style => 1,
175    
176 wakaba 1.38 br => 1, q => 1, cite => 1, em => 1, strong => 1, small => 1, mark => 1,
177 wakaba 1.29 dfn => 1, abbr => 1, time => 1, progress => 1, meter => 1, code => 1,
178     var => 1, samp => 1, kbd => 1, sub => 1, sup => 1, span => 1, i => 1,
179     b => 1, bdo => 1, script => 1, noscript => 1, 'event-source' => 1,
180     command => 1, font => 1,
181     a => 1,
182     datagrid => 1, ## ISSUE: "Interactive element" in the spec.
183     ## NOTE: |area| is allowed only as a descendant of |map|.
184     area => 1,
185    
186     ins => 1, del => 1,
187    
188 wakaba 1.72 ## NOTE: If there is a |menu| ancestor, phrasing. Otherwise, flow.
189 wakaba 1.29 menu => 1,
190    
191     img => 1, iframe => 1, embed => 1, object => 1, video => 1, audio => 1,
192     canvas => 1,
193     },
194    
195     ## NOTE: Embedded
196     q<http://www.w3.org/1998/Math/MathML> => {math => 1},
197     q<http://www.w3.org/2000/svg> => {svg => 1},
198     };
199    
200 wakaba 1.58 my $HTMLSectioningContent = {
201 wakaba 1.57 $HTML_NS => {
202     section => 1, nav => 1, article => 1, aside => 1,
203     ## NOTE: |body| is only allowed in |html| element.
204     body => 1,
205     },
206     };
207    
208 wakaba 1.58 my $HTMLSectioningRoot = {
209 wakaba 1.29 $HTML_NS => {
210 wakaba 1.58 blockquote => 1, datagrid => 1, figure => 1, td => 1,
211 wakaba 1.29 },
212     };
213    
214     my $HTMLHeadingContent = {
215     $HTML_NS => {
216     h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1, header => 1,
217     },
218     };
219    
220     my $HTMLPhrasingContent = {
221 wakaba 1.72 ## NOTE: All phrasing content is also flow content.
222 wakaba 1.29 $HTML_NS => {
223 wakaba 1.38 br => 1, q => 1, cite => 1, em => 1, strong => 1, small => 1, mark => 1,
224 wakaba 1.29 dfn => 1, abbr => 1, time => 1, progress => 1, meter => 1, code => 1,
225     var => 1, samp => 1, kbd => 1, sub => 1, sup => 1, span => 1, i => 1,
226     b => 1, bdo => 1, script => 1, noscript => 1, 'event-source' => 1,
227     command => 1, font => 1,
228     a => 1,
229     datagrid => 1, ## ISSUE: "Interactive element" in the spec.
230     ## NOTE: |area| is allowed only as a descendant of |map|.
231     area => 1,
232    
233     ## NOTE: Transparent.
234     ins => 1, del => 1,
235    
236 wakaba 1.72 ## NOTE: If there is a |menu| ancestor, phrasing. Otherwise, flow.
237 wakaba 1.29 menu => 1,
238    
239     img => 1, iframe => 1, embed => 1, object => 1, video => 1, audio => 1,
240     canvas => 1,
241 wakaba 1.56
242     ## NOTE: WF2
243     input => 1, ## NOTE: type=hidden
244     datalist => 1, ## NOTE: block | where |select| allowed
245 wakaba 1.29 },
246    
247     ## NOTE: Embedded
248     q<http://www.w3.org/1998/Math/MathML> => {math => 1},
249     q<http://www.w3.org/2000/svg> => {svg => 1},
250    
251     ## NOTE: And non-inter-element-whitespace text nodes.
252     };
253    
254 wakaba 1.40 ## $HTMLEmbeddedContent: See Whatpm::ContentChecker.
255 wakaba 1.29
256     my $HTMLInteractiveContent = {
257     $HTML_NS => {
258     a => 1,
259 wakaba 1.36 datagrid => 1, ## ISSUE: Categorized as "Inetractive element"
260 wakaba 1.29 },
261     };
262    
263 wakaba 1.36 ## NOTE: $HTMLTransparentElements: See Whatpm::ContentChecker.
264     ## NOTE: Semi-transparent elements: See Whatpm::ContentChecker.
265    
266     ## -- Common attribute syntacx checkers
267    
268 wakaba 1.1 our $AttrChecker;
269 wakaba 1.82 our $AttrStatus;
270 wakaba 1.1
271     my $GetHTMLEnumeratedAttrChecker = sub {
272     my $states = shift; # {value => conforming ? 1 : -1}
273     return sub {
274     my ($self, $attr) = @_;
275     my $value = lc $attr->value; ## TODO: ASCII case insensitibility?
276     if ($states->{$value} > 0) {
277     #
278     } elsif ($states->{$value}) {
279     $self->{onerror}->(node => $attr, type => 'enumerated:non-conforming');
280     } else {
281     $self->{onerror}->(node => $attr, type => 'enumerated:invalid');
282     }
283     };
284     }; # $GetHTMLEnumeratedAttrChecker
285    
286     my $GetHTMLBooleanAttrChecker = sub {
287     my $local_name = shift;
288     return sub {
289     my ($self, $attr) = @_;
290 wakaba 1.88 my $value = lc $attr->value; ## TODO: case
291 wakaba 1.1 unless ($value eq $local_name or $value eq '') {
292 wakaba 1.88 $self->{onerror}->(node => $attr, type => 'boolean:invalid',
293     level => $self->{must_level});
294 wakaba 1.1 }
295     };
296     }; # $GetHTMLBooleanAttrChecker
297    
298 wakaba 1.8 ## Unordered set of space-separated tokens
299 wakaba 1.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 wakaba 1.95 ## NOTE: A modified copy of the code below is in |datagrid| checker.
1373 wakaba 1.40 if ($element_state->{has_significant}) {
1374 wakaba 1.46 $item->{real_parent_state}->{has_significant} = 1;
1375 wakaba 1.40 } elsif ($item->{transparent}) {
1376     #
1377     } else {
1378     $self->{onerror}->(node => $item->{node},
1379     level => $self->{should_level},
1380     type => 'no significant content');
1381     }
1382     },
1383     );
1384    
1385     my %HTMLPhrasingContentChecker = (
1386     %HTMLChecker,
1387     check_child_element => sub {
1388     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1389     $child_is_transparent, $element_state) = @_;
1390     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
1391     $self->{onerror}->(node => $child_el,
1392     type => 'element not allowed:minus',
1393     level => $self->{must_level});
1394     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1395     #
1396     } elsif ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
1397     #
1398     } else {
1399     $self->{onerror}->(node => $child_el,
1400     type => 'element not allowed:phrasing',
1401     level => $self->{must_level});
1402     }
1403     },
1404 wakaba 1.72 check_end => $HTMLFlowContentChecker{check_end},
1405 wakaba 1.40 ## NOTE: The definition for |li| assumes that the only differences
1406 wakaba 1.72 ## between flow and phrasing content checkers are |check_child_element|
1407 wakaba 1.40 ## and |check_child_text|.
1408     );
1409    
1410 wakaba 1.72 my %HTMLTransparentChecker = %HTMLFlowContentChecker;
1411 wakaba 1.40 ## ISSUE: Significant content rule should be applied to transparent element
1412 wakaba 1.46 ## with parent?
1413 wakaba 1.40
1414 wakaba 1.1 our $Element;
1415     our $ElementDefault;
1416    
1417     $Element->{$HTML_NS}->{''} = {
1418 wakaba 1.40 %HTMLChecker,
1419 wakaba 1.1 };
1420    
1421     $Element->{$HTML_NS}->{html} = {
1422 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1423 wakaba 1.1 is_root => 1,
1424 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
1425 wakaba 1.16 manifest => $HTMLURIAttrChecker,
1426 wakaba 1.67 version => sub {
1427     ## NOTE: According to HTML4 prose, this is a "cdata" attribute.
1428     ## Though DTDs of various versions of HTML define the attribute
1429     ## as |#FIXED|, this conformance checker does no check for
1430     ## the attribute value, since what kind of check should be done
1431     ## is unknown.
1432     },
1433 wakaba 1.49 }, {
1434     %HTMLAttrStatus,
1435 wakaba 1.82 %XHTML2CommonAttrStatus,
1436     class => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_HTML2X_RFC,
1437     dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1438     id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
1439 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1440     manifest => FEATURE_HTML5_DEFAULT,
1441 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
1442 wakaba 1.82 version => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1443 wakaba 1.1 }),
1444 wakaba 1.40 check_start => sub {
1445     my ($self, $item, $element_state) = @_;
1446     $element_state->{phase} = 'before head';
1447 wakaba 1.79
1448 wakaba 1.66 $element_state->{uri_info}->{manifest}->{type}->{resource} = 1;
1449 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
1450     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
1451 wakaba 1.40 },
1452     check_child_element => sub {
1453     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1454     $child_is_transparent, $element_state) = @_;
1455     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
1456     $self->{onerror}->(node => $child_el,
1457     type => 'element not allowed:minus',
1458     level => $self->{must_level});
1459     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1460     #
1461     } elsif ($element_state->{phase} eq 'before head') {
1462     if ($child_nsuri eq $HTML_NS and $child_ln eq 'head') {
1463     $element_state->{phase} = 'after head';
1464     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'body') {
1465     $self->{onerror}->(node => $child_el,
1466     type => 'ps element missing:head');
1467     $element_state->{phase} = 'after body';
1468     } else {
1469     $self->{onerror}->(node => $child_el,
1470     type => 'element not allowed');
1471     }
1472     } elsif ($element_state->{phase} eq 'after head') {
1473     if ($child_nsuri eq $HTML_NS and $child_ln eq 'body') {
1474     $element_state->{phase} = 'after body';
1475     } else {
1476     $self->{onerror}->(node => $child_el,
1477     type => 'element not allowed');
1478     }
1479     } elsif ($element_state->{phase} eq 'after body') {
1480     $self->{onerror}->(node => $child_el,
1481     type => 'element not allowed');
1482     } else {
1483     die "check_child_element: Bad |html| phase: $element_state->{phase}";
1484     }
1485     },
1486     check_child_text => sub {
1487     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1488     if ($has_significant) {
1489     $self->{onerror}->(node => $child_node,
1490     type => 'character not allowed');
1491     }
1492     },
1493     check_end => sub {
1494     my ($self, $item, $element_state) = @_;
1495     if ($element_state->{phase} eq 'after body') {
1496     #
1497     } elsif ($element_state->{phase} eq 'before head') {
1498     $self->{onerror}->(node => $item->{node},
1499     type => 'child element missing:head');
1500     $self->{onerror}->(node => $item->{node},
1501     type => 'child element missing:body');
1502     } elsif ($element_state->{phase} eq 'after head') {
1503     $self->{onerror}->(node => $item->{node},
1504     type => 'child element missing:body');
1505     } else {
1506     die "check_end: Bad |html| phase: $element_state->{phase}";
1507     }
1508 wakaba 1.1
1509 wakaba 1.40 $HTMLChecker{check_end}->(@_);
1510     },
1511     };
1512 wakaba 1.25
1513 wakaba 1.40 $Element->{$HTML_NS}->{head} = {
1514 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1515 wakaba 1.67 check_attrs => $GetHTMLAttrsChecker->({
1516     profile => $HTMLSpaceURIsAttrChecker, ## NOTE: MUST be profile URIs.
1517     }, {
1518 wakaba 1.49 %HTMLAttrStatus,
1519 wakaba 1.82 %XHTML2CommonAttrStatus,
1520     class => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_HTML2X_RFC,
1521     dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1522     id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
1523 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1524 wakaba 1.49 profile => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC,
1525     }),
1526 wakaba 1.40 check_child_element => sub {
1527     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1528     $child_is_transparent, $element_state) = @_;
1529     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
1530     $self->{onerror}->(node => $child_el,
1531     type => 'element not allowed:minus',
1532     level => $self->{must_level});
1533     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1534     #
1535     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'title') {
1536     unless ($element_state->{has_title}) {
1537     $element_state->{has_title} = 1;
1538     } else {
1539     $self->{onerror}->(node => $child_el,
1540     type => 'element not allowed:head title',
1541     level => $self->{must_level});
1542     }
1543     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') {
1544     if ($child_el->has_attribute_ns (undef, 'scoped')) {
1545     $self->{onerror}->(node => $child_el,
1546     type => 'element not allowed:head style',
1547     level => $self->{must_level});
1548 wakaba 1.1 }
1549 wakaba 1.40 } elsif ($HTMLMetadataContent->{$child_nsuri}->{$child_ln}) {
1550     #
1551    
1552     ## NOTE: |meta| is a metadata content. However, strictly speaking,
1553     ## a |meta| element with none of |charset|, |name|,
1554     ## or |http-equiv| attribute is not allowed. It is non-conforming
1555     ## anyway.
1556 wakaba 1.56
1557     ## TODO: |form| MUST be empty and in XML [WF2].
1558 wakaba 1.40 } else {
1559     $self->{onerror}->(node => $child_el,
1560     type => 'element not allowed:metadata',
1561     level => $self->{must_level});
1562     }
1563     $element_state->{in_head_original} = $self->{flag}->{in_head};
1564     $self->{flag}->{in_head} = 1;
1565     },
1566     check_child_text => sub {
1567     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1568     if ($has_significant) {
1569     $self->{onerror}->(node => $child_node, type => 'character not allowed');
1570 wakaba 1.1 }
1571 wakaba 1.40 },
1572     check_end => sub {
1573     my ($self, $item, $element_state) = @_;
1574     unless ($element_state->{has_title}) {
1575     $self->{onerror}->(node => $item->{node},
1576     type => 'child element missing:title');
1577 wakaba 1.1 }
1578 wakaba 1.40 $self->{flag}->{in_head} = $element_state->{in_head_original};
1579 wakaba 1.1
1580 wakaba 1.40 $HTMLChecker{check_end}->(@_);
1581 wakaba 1.1 },
1582     };
1583    
1584 wakaba 1.40 $Element->{$HTML_NS}->{title} = {
1585     %HTMLTextChecker,
1586 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1587 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
1588     %HTMLAttrStatus,
1589 wakaba 1.82 %XHTML2CommonAttrStatus,
1590     class => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_HTML2X_RFC,
1591     dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1592     id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
1593 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1594 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
1595 wakaba 1.49 }),
1596 wakaba 1.40 };
1597 wakaba 1.1
1598 wakaba 1.40 $Element->{$HTML_NS}->{base} = {
1599 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1600 wakaba 1.40 %HTMLEmptyChecker,
1601     check_attrs => sub {
1602     my ($self, $item, $element_state) = @_;
1603 wakaba 1.1
1604 wakaba 1.40 if ($self->{has_base}) {
1605     $self->{onerror}->(node => $item->{node},
1606     type => 'element not allowed:base');
1607     } else {
1608     $self->{has_base} = 1;
1609 wakaba 1.29 }
1610    
1611 wakaba 1.40 my $has_href = $item->{node}->has_attribute_ns (undef, 'href');
1612     my $has_target = $item->{node}->has_attribute_ns (undef, 'target');
1613 wakaba 1.14
1614     if ($self->{has_uri_attr} and $has_href) {
1615 wakaba 1.4 ## ISSUE: Are these examples conforming?
1616     ## <head profile="a b c"><base href> (except for |profile|'s
1617     ## non-conformance)
1618     ## <title xml:base="relative"/><base href/> (maybe it should be)
1619     ## <unknown xmlns="relative"/><base href/> (assuming that
1620     ## |{relative}:unknown| is allowed before XHTML |base| (unlikely, though))
1621     ## <style>@import 'relative';</style><base href>
1622     ## <script>location.href = 'relative';</script><base href>
1623 wakaba 1.14 ## NOTE: <html manifest=".."><head><base href=""/> is conforming as
1624     ## an exception.
1625 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1626 wakaba 1.4 type => 'basehref after URI attribute');
1627     }
1628 wakaba 1.14 if ($self->{has_hyperlink_element} and $has_target) {
1629 wakaba 1.4 ## ISSUE: Are these examples conforming?
1630     ## <head><title xlink:href=""/><base target="name"/></head>
1631     ## <xbl:xbl>...<svg:a href=""/>...</xbl:xbl><base target="name"/>
1632     ## (assuming that |xbl:xbl| is allowed before |base|)
1633     ## NOTE: These are non-conformant anyway because of |head|'s content model:
1634     ## <link href=""/><base target="name"/>
1635     ## <link rel=unknown href=""><base target=name>
1636 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1637 wakaba 1.4 type => 'basetarget after hyperlink');
1638     }
1639    
1640 wakaba 1.14 if (not $has_href and not $has_target) {
1641 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1642 wakaba 1.14 type => 'attribute missing:href|target');
1643     }
1644    
1645 wakaba 1.66 $element_state->{uri_info}->{href}->{type}->{base} = 1;
1646    
1647 wakaba 1.4 return $GetHTMLAttrsChecker->({
1648     href => $HTMLURIAttrChecker,
1649     target => $HTMLTargetAttrChecker,
1650 wakaba 1.49 }, {
1651     %HTMLAttrStatus,
1652 wakaba 1.50 href => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1653     id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1654     target => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1655 wakaba 1.40 })->($self, $item, $element_state);
1656 wakaba 1.4 },
1657 wakaba 1.1 };
1658    
1659     $Element->{$HTML_NS}->{link} = {
1660 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1661 wakaba 1.40 %HTMLEmptyChecker,
1662     check_attrs => sub {
1663     my ($self, $item, $element_state) = @_;
1664 wakaba 1.1 $GetHTMLAttrsChecker->({
1665 wakaba 1.91 charset => sub {
1666     my ($self, $attr) = @_;
1667     $HTMLCharsetChecker->($attr->value, @_);
1668     },
1669 wakaba 1.1 href => $HTMLURIAttrChecker,
1670 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(0, $item, @_) },
1671 wakaba 1.92 rev => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(),
1672 wakaba 1.1 media => $HTMLMQAttrChecker,
1673     hreflang => $HTMLLanguageTagAttrChecker,
1674 wakaba 1.70 target => $HTMLTargetAttrChecker,
1675 wakaba 1.1 type => $HTMLIMTAttrChecker,
1676     ## NOTE: Though |title| has special semantics,
1677     ## syntactically same as the |title| as global attribute.
1678 wakaba 1.49 }, {
1679     %HTMLAttrStatus,
1680 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
1681 wakaba 1.91 charset => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC,
1682     ## NOTE: |charset| attribute had been part of HTML5 spec though
1683     ## it had been commented out.
1684 wakaba 1.82 href => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1685     hreflang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED |
1686     FEATURE_M12N10_REC,
1687 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1688 wakaba 1.82 media => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1689 wakaba 1.61 methods => FEATURE_HTML20_RFC,
1690 wakaba 1.82 rel => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1691     rev => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1692 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
1693 wakaba 1.82 target => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1694 wakaba 1.50 type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1695 wakaba 1.61 urn => FEATURE_HTML20_RFC,
1696 wakaba 1.40 })->($self, $item, $element_state);
1697     if ($item->{node}->has_attribute_ns (undef, 'href')) {
1698     $self->{has_hyperlink_element} = 1 if $item->{has_hyperlink_link_type};
1699 wakaba 1.4 } else {
1700 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1701 wakaba 1.1 type => 'attribute missing:href');
1702     }
1703 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'rel')) {
1704     $self->{onerror}->(node => $item->{node},
1705 wakaba 1.1 type => 'attribute missing:rel');
1706     }
1707     },
1708     };
1709    
1710     $Element->{$HTML_NS}->{meta} = {
1711 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1712 wakaba 1.40 %HTMLEmptyChecker,
1713     check_attrs => sub {
1714     my ($self, $item, $element_state) = @_;
1715 wakaba 1.1 my $name_attr;
1716     my $http_equiv_attr;
1717     my $charset_attr;
1718     my $content_attr;
1719 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
1720 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
1721     $attr_ns = '' unless defined $attr_ns;
1722     my $attr_ln = $attr->manakai_local_name;
1723     my $checker;
1724 wakaba 1.73 my $status;
1725 wakaba 1.1 if ($attr_ns eq '') {
1726 wakaba 1.73 $status = {
1727     %HTMLAttrStatus,
1728 wakaba 1.82 %XHTML2CommonAttrStatus,
1729 wakaba 1.73 charset => FEATURE_HTML5_DEFAULT,
1730 wakaba 1.82 content => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED |
1731     FEATURE_M12N10_REC,
1732     dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED |
1733     FEATURE_M12N10_REC,
1734 wakaba 1.73 'http-equiv' => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1735 wakaba 1.82 id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED |
1736     FEATURE_XHTML10_REC,
1737 wakaba 1.73 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1738     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1739     scheme => FEATURE_M12N10_REC,
1740     }->{$attr_ln};
1741    
1742 wakaba 1.1 if ($attr_ln eq 'content') {
1743     $content_attr = $attr;
1744     $checker = 1;
1745     } elsif ($attr_ln eq 'name') {
1746     $name_attr = $attr;
1747     $checker = 1;
1748     } elsif ($attr_ln eq 'http-equiv') {
1749     $http_equiv_attr = $attr;
1750     $checker = 1;
1751     } elsif ($attr_ln eq 'charset') {
1752     $charset_attr = $attr;
1753     $checker = 1;
1754 wakaba 1.67 } elsif ($attr_ln eq 'scheme') {
1755 wakaba 1.71 ## NOTE: <http://suika.fam.cx/2007/html/standards#html-meta-scheme>
1756 wakaba 1.67 $checker = sub {};
1757 wakaba 1.73 } elsif ($attr_ln =~ /^data-/) {
1758     $checker = $HTMLDatasetAttrChecker;
1759     $status = $HTMLDatasetAttrStatus;
1760 wakaba 1.1 } else {
1761     $checker = $HTMLAttrChecker->{$attr_ln}
1762 wakaba 1.67 || $AttrChecker->{$attr_ns}->{$attr_ln}
1763 wakaba 1.1 || $AttrChecker->{$attr_ns}->{''};
1764     }
1765     } else {
1766     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
1767 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
1768     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
1769     || $AttrStatus->{$attr_ns}->{''};
1770     $status = FEATURE_ALLOWED if not defined $status;
1771 wakaba 1.1 }
1772 wakaba 1.62
1773 wakaba 1.1 if ($checker) {
1774 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
1775 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
1776 wakaba 1.54 #
1777 wakaba 1.1 } else {
1778     $self->{onerror}->(node => $attr, level => 'unsupported',
1779     type => 'attribute');
1780 wakaba 1.49 ## ISSUE: No conformance createria for unknown attributes in the spec
1781     }
1782    
1783 wakaba 1.82 $self->_attr_status_info ($attr, $status);
1784 wakaba 1.1 }
1785    
1786     if (defined $name_attr) {
1787     if (defined $http_equiv_attr) {
1788     $self->{onerror}->(node => $http_equiv_attr,
1789     type => 'attribute not allowed');
1790     } elsif (defined $charset_attr) {
1791     $self->{onerror}->(node => $charset_attr,
1792     type => 'attribute not allowed');
1793     }
1794     my $metadata_name = $name_attr->value;
1795     my $metadata_value;
1796     if (defined $content_attr) {
1797     $metadata_value = $content_attr->value;
1798     } else {
1799 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1800 wakaba 1.1 type => 'attribute missing:content');
1801     $metadata_value = '';
1802     }
1803     } elsif (defined $http_equiv_attr) {
1804     if (defined $charset_attr) {
1805     $self->{onerror}->(node => $charset_attr,
1806     type => 'attribute not allowed');
1807     }
1808     unless (defined $content_attr) {
1809 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1810 wakaba 1.1 type => 'attribute missing:content');
1811     }
1812     } elsif (defined $charset_attr) {
1813     if (defined $content_attr) {
1814     $self->{onerror}->(node => $content_attr,
1815     type => 'attribute not allowed');
1816     }
1817     } else {
1818     if (defined $content_attr) {
1819     $self->{onerror}->(node => $content_attr,
1820     type => 'attribute not allowed');
1821 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1822 wakaba 1.1 type => 'attribute missing:name|http-equiv');
1823     } else {
1824 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1825 wakaba 1.1 type => 'attribute missing:name|http-equiv|charset');
1826     }
1827     }
1828    
1829 wakaba 1.32 my $check_charset_decl = sub () {
1830 wakaba 1.40 my $parent = $item->{node}->manakai_parent_element;
1831 wakaba 1.29 if ($parent and $parent eq $parent->owner_document->manakai_head) {
1832     for my $el (@{$parent->child_nodes}) {
1833     next unless $el->node_type == 1; # ELEMENT_NODE
1834 wakaba 1.40 unless ($el eq $item->{node}) {
1835 wakaba 1.29 ## NOTE: Not the first child element.
1836 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1837 wakaba 1.32 type => 'element not allowed:meta charset',
1838     level => $self->{must_level});
1839 wakaba 1.29 }
1840     last;
1841     ## NOTE: Entity references are not supported.
1842     }
1843     } else {
1844 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1845 wakaba 1.32 type => 'element not allowed:meta charset',
1846     level => $self->{must_level});
1847 wakaba 1.29 }
1848    
1849 wakaba 1.40 unless ($item->{node}->owner_document->manakai_is_html) {
1850     $self->{onerror}->(node => $item->{node},
1851 wakaba 1.32 type => 'in XML:charset',
1852     level => $self->{must_level});
1853 wakaba 1.1 }
1854 wakaba 1.32 }; # $check_charset_decl
1855 wakaba 1.21
1856 wakaba 1.32 my $check_charset = sub ($$) {
1857     my ($attr, $charset_value) = @_;
1858 wakaba 1.21
1859 wakaba 1.91 my $charset;
1860     ($charset, $charset_value)
1861     = $HTMLCharsetChecker->($charset_value, $self, $attr);
1862    
1863 wakaba 1.40 my $ic = $item->{node}->owner_document->input_encoding;
1864 wakaba 1.21 if (defined $ic) {
1865     ## TODO: Test for this case
1866     my $ic_charset = $Message::Charset::Info::IANACharset->{$ic};
1867     if ($charset ne $ic_charset) {
1868 wakaba 1.32 $self->{onerror}->(node => $attr,
1869 wakaba 1.21 type => 'mismatched charset name:'.$ic.
1870 wakaba 1.32 ':'.$charset_value, ## TODO: This should be a |value| value.
1871     level => $self->{must_level});
1872 wakaba 1.21 }
1873     } else {
1874     ## NOTE: MUST, but not checkable, since the document is not originally
1875     ## in serialized form (or the parser does not preserve the input
1876     ## encoding information).
1877 wakaba 1.32 $self->{onerror}->(node => $attr,
1878     type => 'mismatched charset name::'.$charset_value, ## TODO: |value|
1879 wakaba 1.21 level => 'unsupported');
1880     }
1881    
1882 wakaba 1.32 if ($attr->get_user_data ('manakai_has_reference')) {
1883     $self->{onerror}->(node => $attr,
1884 wakaba 1.22 type => 'character reference in charset',
1885     level => $self->{must_level});
1886     }
1887 wakaba 1.32 }; # $check_charset
1888    
1889     ## TODO: metadata conformance
1890    
1891     ## TODO: pragma conformance
1892     if (defined $http_equiv_attr) { ## An enumerated attribute
1893     my $keyword = lc $http_equiv_attr->value; ## TODO: ascii case?
1894 wakaba 1.33
1895 wakaba 1.85 if ($self->{has_http_equiv}->{$keyword}) {
1896     $self->{onerror}->(type => 'duplicate http-equiv', value => $keyword,
1897     node => $http_equiv_attr,
1898     level => $self->{must_level});
1899     } else {
1900     $self->{has_http_equiv}->{$keyword} = 1;
1901     }
1902    
1903     if ($keyword eq 'content-type') {
1904 wakaba 1.58 ## TODO: refs in "text/html; charset=" are not disallowed since rev.1275.
1905 wakaba 1.33
1906 wakaba 1.32 $check_charset_decl->();
1907     if ($content_attr) {
1908     my $content = $content_attr->value;
1909 wakaba 1.58 if ($content =~ m!^[Tt][Ee][Xx][Tt]/[Hh][Tt][Mm][Ll];
1910     [\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
1911     =(.+)\z!sx) {
1912 wakaba 1.32 $check_charset->($content_attr, $1);
1913     } else {
1914     $self->{onerror}->(node => $content_attr,
1915     type => 'meta content-type syntax error',
1916 wakaba 1.85 level => $self->{must_level});
1917     }
1918     }
1919     } elsif ($keyword eq 'default-style') {
1920     ## ISSUE: Not defined yet in the spec.
1921     } elsif ($keyword eq 'refresh') {
1922     if ($content_attr) {
1923     my $content = $content_attr->value;
1924     if ($content =~ /\A[0-9]+\z/) {
1925     ## NOTE: Valid non-negative integer.
1926     #
1927     } elsif ($content =~ s/\A[0-9]+;[\x09-\x0D\x20]+[Uu][Rr][Ll]=//) {
1928     ## ISSUE: Relative references are allowed? (RFC 3987 "IRI" is an absolute reference with optional fragment identifier.)
1929     Whatpm::URIChecker->check_iri_reference ($content, sub {
1930     my %opt = @_;
1931     $self->{onerror}->(node => $content_attr, level => $opt{level},
1932     type => 'URI::'.$opt{type}.
1933 wakaba 1.86 (defined $opt{position} ? ':'.$opt{position} : ''),
1934     value => $content);
1935 wakaba 1.85 });
1936     $self->{has_uri_attr} = 1; ## NOTE: One of "attributes with URIs".
1937    
1938     $element_state->{uri_info}->{content}->{node} = $content_attr;
1939     $element_state->{uri_info}->{content}->{type}->{hyperlink} = 1;
1940     ## TODO: absolute
1941     push @{$self->{return}->{uri}->{$content} ||= []},
1942     $element_state->{uri_info}->{content};
1943     } else {
1944     $self->{onerror}->(node => $content_attr,
1945     type => 'refresh:syntax error',
1946 wakaba 1.32 level => $self->{must_level});
1947     }
1948     }
1949     } else {
1950     $self->{onerror}->(node => $http_equiv_attr,
1951     type => 'enumerated:invalid');
1952     }
1953     }
1954    
1955     if (defined $charset_attr) {
1956     $check_charset_decl->();
1957     $check_charset->($charset_attr, $charset_attr->value);
1958 wakaba 1.1 }
1959     },
1960     };
1961    
1962     $Element->{$HTML_NS}->{style} = {
1963 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1964 wakaba 1.40 %HTMLChecker,
1965     check_attrs => $GetHTMLAttrsChecker->({
1966 wakaba 1.1 type => $HTMLIMTAttrChecker, ## TODO: MUST be a styling language
1967     media => $HTMLMQAttrChecker,
1968     scoped => $GetHTMLBooleanAttrChecker->('scoped'),
1969     ## NOTE: |title| has special semantics for |style|s, but is syntactically
1970     ## not different
1971 wakaba 1.49 }, {
1972     %HTMLAttrStatus,
1973 wakaba 1.82 %XHTML2CommonAttrStatus,
1974     dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1975     disabled => FEATURE_XHTML2_ED,
1976     id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
1977 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1978 wakaba 1.82 media => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1979 wakaba 1.50 scoped => FEATURE_HTML5_DEFAULT,
1980 wakaba 1.82 title => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1981     type => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1982 wakaba 1.1 }),
1983 wakaba 1.40 check_start => sub {
1984     my ($self, $item, $element_state) = @_;
1985    
1986 wakaba 1.27 ## NOTE: |html:style| itself has no conformance creteria on content model.
1987 wakaba 1.40 my $type = $item->{node}->get_attribute_ns (undef, 'type');
1988 wakaba 1.93 $type = 'text/css' unless defined $type;
1989     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]) {
1990     $type = "$1/$2";
1991     $type =~ tr/A-Z/a-z/; ## NOTE: ASCII case-insensitive
1992     } else {
1993     ## NOTE: We don't know how parameters are handled by UAs. According to
1994     ## HTML5 specification, <style> with unknown parameters in |type=""|
1995     ## must be ignored.
1996     undef $type;
1997     }
1998     if (not defined $type) {
1999     $element_state->{allow_element} = 1; # invalid type=""
2000     } elsif ($type eq 'text/css') {
2001 wakaba 1.40 $element_state->{allow_element} = 0;
2002 wakaba 1.93 #} elsif ($type =~ m![/+][Xx][Mm][Ll]\z!) {
2003     # ## NOTE: There is no definition for "XML-based styling language" in HTML5
2004     # $element_state->{allow_element} = 1;
2005 wakaba 1.40 } else {
2006     $element_state->{allow_element} = 1; # unknown
2007     }
2008 wakaba 1.93 $element_state->{style_type} = $type;
2009 wakaba 1.79
2010     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2011     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2012 wakaba 1.40 },
2013     check_child_element => sub {
2014     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2015     $child_is_transparent, $element_state) = @_;
2016     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
2017     $self->{onerror}->(node => $child_el,
2018     type => 'element not allowed:minus',
2019     level => $self->{must_level});
2020     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2021     #
2022     } elsif ($element_state->{allow_element}) {
2023     #
2024     } else {
2025     $self->{onerror}->(node => $child_el, type => 'element not allowed');
2026     }
2027     },
2028     check_child_text => sub {
2029     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2030     $element_state->{text} .= $child_node->text_content;
2031     },
2032     check_end => sub {
2033     my ($self, $item, $element_state) = @_;
2034 wakaba 1.93 if (not defined $element_state->{style_type}) {
2035     ## NOTE: Invalid type=""
2036     #
2037     } elsif ($element_state->{style_type} eq 'text/css') {
2038 wakaba 1.40 $self->{onsubdoc}->({s => $element_state->{text},
2039     container_node => $item->{node},
2040 wakaba 1.28 media_type => 'text/css', is_char_string => 1});
2041 wakaba 1.93 } elsif ($element_state->{style_type} =~ m![+/][Xx][Mm][Ll]\z!) {
2042     ## NOTE: XML content should be checked by THIS instance of checker
2043     ## as part of normal tree validation. However, we don't know of any
2044     ## XML-based styling language that can be used in HTML <style> element,
2045     ## such that we throw a "style language not supported" error.
2046 wakaba 1.40 $self->{onerror}->(node => $item->{node}, level => 'unsupported',
2047     type => 'style:'.$element_state->{style_type});
2048 wakaba 1.93 } else {
2049     ## NOTE: Should we raise some kind of error for,
2050     ## say, <style type="text/plaion">?
2051     $self->{onsubdoc}->({s => $element_state->{text},
2052     container_node => $item->{node},
2053     media_type => $element_state->{style_type},
2054     is_char_string => 1});
2055 wakaba 1.27 }
2056 wakaba 1.40
2057     $HTMLChecker{check_end}->(@_);
2058 wakaba 1.1 },
2059     };
2060 wakaba 1.25 ## ISSUE: Relationship to significant content check?
2061 wakaba 1.1
2062     $Element->{$HTML_NS}->{body} = {
2063 wakaba 1.72 %HTMLFlowContentChecker,
2064 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2065 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2066     alink => $HTMLColorAttrChecker,
2067     background => $HTMLURIAttrChecker,
2068     bgcolor => $HTMLColorAttrChecker,
2069     link => $HTMLColorAttrChecker,
2070     text => $HTMLColorAttrChecker,
2071     vlink => $HTMLColorAttrChecker,
2072     }, {
2073 wakaba 1.49 %HTMLAttrStatus,
2074 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2075 wakaba 1.49 alink => FEATURE_M12N10_REC_DEPRECATED,
2076     background => FEATURE_M12N10_REC_DEPRECATED,
2077     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
2078 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2079 wakaba 1.49 link => FEATURE_M12N10_REC_DEPRECATED,
2080 wakaba 1.50 onload => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2081     onunload => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2082 wakaba 1.49 text => FEATURE_M12N10_REC_DEPRECATED,
2083     vlink => FEATURE_M12N10_REC_DEPRECATED,
2084     }),
2085 wakaba 1.68 check_start => sub {
2086     my ($self, $item, $element_state) = @_;
2087    
2088     $element_state->{uri_info}->{background}->{type}->{embedded} = 1;
2089 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2090     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2091 wakaba 1.68 },
2092 wakaba 1.1 };
2093    
2094     $Element->{$HTML_NS}->{section} = {
2095 wakaba 1.72 %HTMLFlowContentChecker,
2096 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED,
2097     check_attrs => $GetHTMLAttrsChecker->({
2098     }, {
2099     %HTMLAttrStatus,
2100     %XHTML2CommonAttrStatus,
2101     }),
2102 wakaba 1.1 };
2103    
2104     $Element->{$HTML_NS}->{nav} = {
2105 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2106 wakaba 1.72 %HTMLFlowContentChecker,
2107 wakaba 1.1 };
2108    
2109     $Element->{$HTML_NS}->{article} = {
2110 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2111 wakaba 1.72 %HTMLFlowContentChecker,
2112 wakaba 1.1 };
2113    
2114     $Element->{$HTML_NS}->{blockquote} = {
2115 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2116 wakaba 1.72 %HTMLFlowContentChecker,
2117 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2118 wakaba 1.1 cite => $HTMLURIAttrChecker,
2119 wakaba 1.49 }, {
2120     %HTMLAttrStatus,
2121 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2122 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2123 wakaba 1.82 cite => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2124 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2125 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2126 wakaba 1.1 }),
2127 wakaba 1.66 check_start => sub {
2128     my ($self, $item, $element_state) = @_;
2129    
2130     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
2131 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2132     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2133 wakaba 1.66 },
2134 wakaba 1.1 };
2135    
2136     $Element->{$HTML_NS}->{aside} = {
2137 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2138 wakaba 1.72 %HTMLFlowContentChecker,
2139 wakaba 1.1 };
2140    
2141     $Element->{$HTML_NS}->{h1} = {
2142 wakaba 1.40 %HTMLPhrasingContentChecker,
2143 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2144 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2145     align => $GetHTMLEnumeratedAttrChecker->({
2146     left => 1, center => 1, right => 1, justify => 1,
2147     }),
2148     }, {
2149 wakaba 1.49 %HTMLAttrStatus,
2150 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2151 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
2152 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2153 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2154 wakaba 1.49 }),
2155 wakaba 1.40 check_start => sub {
2156     my ($self, $item, $element_state) = @_;
2157     $self->{flag}->{has_hn} = 1;
2158 wakaba 1.79
2159     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2160     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2161 wakaba 1.1 },
2162     };
2163    
2164 wakaba 1.40 $Element->{$HTML_NS}->{h2} = {%{$Element->{$HTML_NS}->{h1}}};
2165 wakaba 1.1
2166 wakaba 1.40 $Element->{$HTML_NS}->{h3} = {%{$Element->{$HTML_NS}->{h1}}};
2167 wakaba 1.1
2168 wakaba 1.40 $Element->{$HTML_NS}->{h4} = {%{$Element->{$HTML_NS}->{h1}}};
2169 wakaba 1.1
2170 wakaba 1.40 $Element->{$HTML_NS}->{h5} = {%{$Element->{$HTML_NS}->{h1}}};
2171 wakaba 1.1
2172 wakaba 1.40 $Element->{$HTML_NS}->{h6} = {%{$Element->{$HTML_NS}->{h1}}};
2173 wakaba 1.1
2174 wakaba 1.29 ## TODO: Explicit sectioning is "encouraged".
2175    
2176 wakaba 1.1 $Element->{$HTML_NS}->{header} = {
2177 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2178 wakaba 1.72 %HTMLFlowContentChecker,
2179 wakaba 1.40 check_start => sub {
2180     my ($self, $item, $element_state) = @_;
2181     $self->_add_minus_elements ($element_state,
2182     {$HTML_NS => {qw/header 1 footer 1/}},
2183 wakaba 1.58 $HTMLSectioningContent);
2184 wakaba 1.40 $element_state->{has_hn_original} = $self->{flag}->{has_hn};
2185     $self->{flag}->{has_hn} = 0;
2186 wakaba 1.79
2187     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2188     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2189 wakaba 1.40 },
2190     check_end => sub {
2191     my ($self, $item, $element_state) = @_;
2192     $self->_remove_minus_elements ($element_state);
2193     unless ($self->{flag}->{has_hn}) {
2194     $self->{onerror}->(node => $item->{node},
2195     type => 'element missing:hn');
2196     }
2197     $self->{flag}->{has_hn} ||= $element_state->{has_hn_original};
2198 wakaba 1.1
2199 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
2200 wakaba 1.1 },
2201 wakaba 1.40 ## ISSUE: <header><del><h1>...</h1></del></header> is conforming?
2202 wakaba 1.1 };
2203    
2204     $Element->{$HTML_NS}->{footer} = {
2205 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2206 wakaba 1.72 %HTMLFlowContentChecker,
2207 wakaba 1.40 check_start => sub {
2208     my ($self, $item, $element_state) = @_;
2209     $self->_add_minus_elements ($element_state,
2210     {$HTML_NS => {footer => 1}},
2211 wakaba 1.58 $HTMLSectioningContent,
2212 wakaba 1.57 $HTMLHeadingContent);
2213 wakaba 1.79
2214     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2215     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2216 wakaba 1.40 },
2217     check_end => sub {
2218     my ($self, $item, $element_state) = @_;
2219     $self->_remove_minus_elements ($element_state);
2220 wakaba 1.1
2221 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
2222 wakaba 1.1 },
2223     };
2224    
2225     $Element->{$HTML_NS}->{address} = {
2226 wakaba 1.72 %HTMLFlowContentChecker,
2227 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2228 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2229     %HTMLAttrStatus,
2230 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2231 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2232 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2233 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2234     sdapref => FEATURE_HTML20_RFC,
2235 wakaba 1.49 }),
2236 wakaba 1.40 check_start => sub {
2237     my ($self, $item, $element_state) = @_;
2238     $self->_add_minus_elements ($element_state,
2239     {$HTML_NS => {footer => 1, address => 1}},
2240     $HTMLSectioningContent, $HTMLHeadingContent);
2241 wakaba 1.79
2242     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2243     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2244 wakaba 1.40 },
2245     check_end => sub {
2246     my ($self, $item, $element_state) = @_;
2247     $self->_remove_minus_elements ($element_state);
2248 wakaba 1.29
2249 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
2250 wakaba 1.29 },
2251 wakaba 1.1 };
2252    
2253     $Element->{$HTML_NS}->{p} = {
2254 wakaba 1.40 %HTMLPhrasingContentChecker,
2255 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2256 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2257     align => $GetHTMLEnumeratedAttrChecker->({
2258     left => 1, center => 1, right => 1, justify => 1,
2259     }),
2260     }, {
2261 wakaba 1.49 %HTMLAttrStatus,
2262 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2263 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
2264 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2265 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2266 wakaba 1.49 }),
2267 wakaba 1.1 };
2268    
2269     $Element->{$HTML_NS}->{hr} = {
2270 wakaba 1.40 %HTMLEmptyChecker,
2271 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2272 wakaba 1.70 check_attrs => $GetHTMLAttrsChecker->({
2273     ## TODO: HTML4 |align|, |noshade|, |size|, |width|
2274     }, {
2275 wakaba 1.49 %HTMLAttrStatus,
2276     %HTMLM12NCommonAttrStatus,
2277     align => FEATURE_M12N10_REC_DEPRECATED,
2278 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2279 wakaba 1.49 noshade => FEATURE_M12N10_REC_DEPRECATED,
2280 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
2281 wakaba 1.49 size => FEATURE_M12N10_REC_DEPRECATED,
2282     width => FEATURE_M12N10_REC_DEPRECATED,
2283     }),
2284 wakaba 1.1 };
2285    
2286     $Element->{$HTML_NS}->{br} = {
2287 wakaba 1.40 %HTMLEmptyChecker,
2288 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2289 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2290     clear => $GetHTMLEnumeratedAttrChecker->({
2291     left => 1, all => 1, right => 1, none => 1,
2292     }),
2293     }, {
2294 wakaba 1.49 %HTMLAttrStatus,
2295 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2296 wakaba 1.49 clear => FEATURE_M12N10_REC_DEPRECATED,
2297 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2298 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
2299 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2300 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2301 wakaba 1.49 }),
2302 wakaba 1.29 ## NOTE: Blank line MUST NOT be used for presentation purpose.
2303     ## (This requirement is semantic so that we cannot check.)
2304 wakaba 1.1 };
2305    
2306     $Element->{$HTML_NS}->{dialog} = {
2307 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2308 wakaba 1.40 %HTMLChecker,
2309     check_start => sub {
2310     my ($self, $item, $element_state) = @_;
2311     $element_state->{phase} = 'before dt';
2312 wakaba 1.79
2313     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2314     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2315 wakaba 1.40 },
2316     check_child_element => sub {
2317     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2318     $child_is_transparent, $element_state) = @_;
2319     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
2320     $self->{onerror}->(node => $child_el,
2321     type => 'element not allowed:minus',
2322     level => $self->{must_level});
2323     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2324     #
2325     } elsif ($element_state->{phase} eq 'before dt') {
2326     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2327     $element_state->{phase} = 'before dd';
2328     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2329     $self->{onerror}
2330     ->(node => $child_el, type => 'ps element missing:dt');
2331     $element_state->{phase} = 'before dt';
2332     } else {
2333     $self->{onerror}->(node => $child_el, type => 'element not allowed');
2334     }
2335     } elsif ($element_state->{phase} eq 'before dd') {
2336     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2337     $element_state->{phase} = 'before dt';
2338     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2339     $self->{onerror}
2340     ->(node => $child_el, type => 'ps element missing:dd');
2341     $element_state->{phase} = 'before dd';
2342     } else {
2343     $self->{onerror}->(node => $child_el, type => 'element not allowed');
2344 wakaba 1.1 }
2345 wakaba 1.40 } else {
2346     die "check_child_element: Bad |dialog| phase: $element_state->{phase}";
2347     }
2348     },
2349     check_child_text => sub {
2350     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2351     if ($has_significant) {
2352     $self->{onerror}->(node => $child_node, type => 'character not allowed');
2353 wakaba 1.1 }
2354 wakaba 1.40 },
2355     check_end => sub {
2356     my ($self, $item, $element_state) = @_;
2357     if ($element_state->{phase} eq 'before dd') {
2358     $self->{onerror}->(node => $item->{node},
2359     type => 'child element missing:dd');
2360 wakaba 1.1 }
2361 wakaba 1.40
2362     $HTMLChecker{check_end}->(@_);
2363 wakaba 1.1 },
2364     };
2365    
2366     $Element->{$HTML_NS}->{pre} = {
2367 wakaba 1.40 %HTMLPhrasingContentChecker,
2368 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2369 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2370     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
2371     }, {
2372 wakaba 1.49 %HTMLAttrStatus,
2373 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2374 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2375 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2376 wakaba 1.49 width => FEATURE_M12N10_REC_DEPRECATED,
2377     }),
2378 wakaba 1.1 };
2379    
2380     $Element->{$HTML_NS}->{ol} = {
2381 wakaba 1.40 %HTMLChecker,
2382 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2383 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2384 wakaba 1.68 compact => $GetHTMLBooleanAttrChecker->('compact'),
2385 wakaba 1.69 reversed => $GetHTMLBooleanAttrChecker->('reversed'),
2386 wakaba 1.1 start => $HTMLIntegerAttrChecker,
2387 wakaba 1.69 ## TODO: HTML4 |type|
2388 wakaba 1.49 }, {
2389     %HTMLAttrStatus,
2390 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2391 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2392 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2393 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2394 wakaba 1.53 reversed => FEATURE_HTML5_DEFAULT,
2395 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2396 wakaba 1.54 #start => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
2397     start => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2398 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2399 wakaba 1.1 }),
2400 wakaba 1.40 check_child_element => sub {
2401     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2402     $child_is_transparent, $element_state) = @_;
2403     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
2404     $self->{onerror}->(node => $child_el,
2405     type => 'element not allowed:minus',
2406     level => $self->{must_level});
2407     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2408     #
2409     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'li') {
2410     #
2411     } else {
2412     $self->{onerror}->(node => $child_el, type => 'element not allowed');
2413 wakaba 1.1 }
2414 wakaba 1.40 },
2415     check_child_text => sub {
2416     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2417     if ($has_significant) {
2418     $self->{onerror}->(node => $child_node, type => 'character not allowed');
2419 wakaba 1.1 }
2420     },
2421     };
2422    
2423     $Element->{$HTML_NS}->{ul} = {
2424 wakaba 1.40 %{$Element->{$HTML_NS}->{ol}},
2425 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2426 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2427     compact => $GetHTMLBooleanAttrChecker->('compact'),
2428 wakaba 1.69 ## TODO: HTML4 |type|
2429     ## TODO: sdaform, align
2430 wakaba 1.68 }, {
2431 wakaba 1.49 %HTMLAttrStatus,
2432 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2433 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2434 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2435 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2436 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2437 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2438     }),
2439 wakaba 1.1 };
2440    
2441 wakaba 1.64 $Element->{$HTML_NS}->{dir} = {
2442     ## TODO: %block; is not allowed [HTML4] ## TODO: Empty list allowed?
2443     %{$Element->{$HTML_NS}->{ul}},
2444     status => FEATURE_M12N10_REC_DEPRECATED,
2445 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2446     compact => $GetHTMLBooleanAttrChecker->('compact'),
2447     }, {
2448 wakaba 1.64 %HTMLAttrStatus,
2449     %HTMLM12NCommonAttrStatus,
2450     align => FEATURE_HTML2X_RFC,
2451     compact => FEATURE_M12N10_REC_DEPRECATED,
2452     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2453     sdaform => FEATURE_HTML20_RFC,
2454     sdapref => FEATURE_HTML20_RFC,
2455     }),
2456     };
2457    
2458 wakaba 1.1 $Element->{$HTML_NS}->{li} = {
2459 wakaba 1.72 %HTMLFlowContentChecker,
2460 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2461 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2462 wakaba 1.69 ## TODO: HTML4 |type|
2463 wakaba 1.49 value => sub {
2464 wakaba 1.1 my ($self, $attr) = @_;
2465     my $parent = $attr->owner_element->manakai_parent_element;
2466     if (defined $parent) {
2467     my $parent_ns = $parent->namespace_uri;
2468     $parent_ns = '' unless defined $parent_ns;
2469     my $parent_ln = $parent->manakai_local_name;
2470     unless ($parent_ns eq $HTML_NS and $parent_ln eq 'ol') {
2471     $self->{onerror}->(node => $attr, level => 'unsupported',
2472     type => 'attribute');
2473     }
2474     }
2475     $HTMLIntegerAttrChecker->($self, $attr);
2476 wakaba 1.49 }, ## TODO: test
2477     }, {
2478     %HTMLAttrStatus,
2479 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2480 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2481 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2482 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2483 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2484 wakaba 1.55 #value => FEATURE_HTML5_DEFAULT | FEATURE_XHTMLBASIC11_CR |
2485     # FEATURE_M12N10_REC_DEPRECATED,
2486 wakaba 1.82 value => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED |
2487     FEATURE_XHTMLBASIC11_CR | FEATURE_M12N10_REC,
2488 wakaba 1.1 }),
2489 wakaba 1.40 check_child_element => sub {
2490     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2491     $child_is_transparent, $element_state) = @_;
2492     if ($self->{flag}->{in_menu}) {
2493     $HTMLPhrasingContentChecker{check_child_element}->(@_);
2494     } else {
2495 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
2496 wakaba 1.40 }
2497     },
2498     check_child_text => sub {
2499     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2500     if ($self->{flag}->{in_menu}) {
2501     $HTMLPhrasingContentChecker{check_child_text}->(@_);
2502 wakaba 1.1 } else {
2503 wakaba 1.72 $HTMLFlowContentChecker{check_child_text}->(@_);
2504 wakaba 1.1 }
2505     },
2506     };
2507    
2508     $Element->{$HTML_NS}->{dl} = {
2509 wakaba 1.40 %HTMLChecker,
2510 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2511 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2512     compact => $GetHTMLBooleanAttrChecker->('compact'),
2513     }, {
2514 wakaba 1.49 %HTMLAttrStatus,
2515 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2516 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2517 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2518 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2519     sdapref => FEATURE_HTML20_RFC,
2520 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2521     }),
2522 wakaba 1.40 check_start => sub {
2523     my ($self, $item, $element_state) = @_;
2524     $element_state->{phase} = 'before dt';
2525 wakaba 1.79
2526     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2527     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2528 wakaba 1.40 },
2529     check_child_element => sub {
2530     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2531     $child_is_transparent, $element_state) = @_;
2532     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
2533     $self->{onerror}->(node => $child_el,
2534     type => 'element not allowed:minus',
2535     level => $self->{must_level});
2536     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2537     #
2538     } elsif ($element_state->{phase} eq 'in dds') {
2539     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2540     #$element_state->{phase} = 'in dds';
2541     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2542     $element_state->{phase} = 'in dts';
2543     } else {
2544     $self->{onerror}->(node => $child_el, type => 'element not allowed');
2545     }
2546     } elsif ($element_state->{phase} eq 'in dts') {
2547     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2548     #$element_state->{phase} = 'in dts';
2549     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2550     $element_state->{phase} = 'in dds';
2551     } else {
2552     $self->{onerror}->(node => $child_el, type => 'element not allowed');
2553     }
2554     } elsif ($element_state->{phase} eq 'before dt') {
2555     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2556     $element_state->{phase} = 'in dts';
2557     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2558     $self->{onerror}
2559     ->(node => $child_el, type => 'ps element missing:dt');
2560     $element_state->{phase} = 'in dds';
2561     } else {
2562     $self->{onerror}->(node => $child_el, type => 'element not allowed');
2563 wakaba 1.1 }
2564 wakaba 1.40 } else {
2565     die "check_child_element: Bad |dl| phase: $element_state->{phase}";
2566 wakaba 1.1 }
2567 wakaba 1.40 },
2568     check_child_text => sub {
2569     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2570     if ($has_significant) {
2571     $self->{onerror}->(node => $child_node, type => 'character not allowed');
2572     }
2573     },
2574     check_end => sub {
2575     my ($self, $item, $element_state) = @_;
2576     if ($element_state->{phase} eq 'in dts') {
2577     $self->{onerror}->(node => $item->{node},
2578     type => 'child element missing:dd');
2579 wakaba 1.1 }
2580    
2581 wakaba 1.40 $HTMLChecker{check_end}->(@_);
2582 wakaba 1.1 },
2583     };
2584    
2585     $Element->{$HTML_NS}->{dt} = {
2586 wakaba 1.40 %HTMLPhrasingContentChecker,
2587 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2588 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2589     %HTMLAttrStatus,
2590 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2591 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2592 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2593 wakaba 1.49 }),
2594 wakaba 1.1 };
2595    
2596     $Element->{$HTML_NS}->{dd} = {
2597 wakaba 1.72 %HTMLFlowContentChecker,
2598 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2599 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2600     %HTMLAttrStatus,
2601 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2602 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2603 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2604 wakaba 1.49 }),
2605 wakaba 1.1 };
2606    
2607     $Element->{$HTML_NS}->{a} = {
2608 wakaba 1.40 %HTMLPhrasingContentChecker,
2609 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2610 wakaba 1.40 check_attrs => sub {
2611     my ($self, $item, $element_state) = @_;
2612 wakaba 1.1 my %attr;
2613 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
2614 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
2615     $attr_ns = '' unless defined $attr_ns;
2616     my $attr_ln = $attr->manakai_local_name;
2617     my $checker;
2618 wakaba 1.73 my $status;
2619 wakaba 1.1 if ($attr_ns eq '') {
2620 wakaba 1.73 $status = {
2621     %HTMLAttrStatus,
2622 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2623 wakaba 1.73 accesskey => FEATURE_M12N10_REC,
2624     charset => FEATURE_M12N10_REC,
2625 wakaba 1.82 coords => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2626 wakaba 1.73 cryptopts => FEATURE_RFC2659,
2627     dn => FEATURE_RFC2659,
2628 wakaba 1.82 href => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2629     hreflang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2630 wakaba 1.73 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2631 wakaba 1.82 media => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED,
2632 wakaba 1.73 methods => FEATURE_HTML20_RFC,
2633     name => FEATURE_M12N10_REC_DEPRECATED,
2634     nonce => FEATURE_RFC2659,
2635     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2636     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2637     ping => FEATURE_HTML5_DEFAULT,
2638 wakaba 1.82 rel => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2639     rev => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2640 wakaba 1.73 sdapref => FEATURE_HTML20_RFC,
2641 wakaba 1.82 shape => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2642 wakaba 1.73 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2643 wakaba 1.82 target => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2644 wakaba 1.73 type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2645     urn => FEATURE_HTML20_RFC,
2646     }->{$attr_ln};
2647    
2648 wakaba 1.1 $checker = {
2649 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
2650 wakaba 1.91 charset => sub {
2651     my ($self, $attr) = @_;
2652     $HTMLCharsetChecker->($attr->value, @_);
2653     },
2654 wakaba 1.70 ## TODO: HTML4 |coords|
2655 wakaba 1.1 target => $HTMLTargetAttrChecker,
2656     href => $HTMLURIAttrChecker,
2657     ping => $HTMLSpaceURIsAttrChecker,
2658 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) },
2659 wakaba 1.92 rev => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(),
2660 wakaba 1.70 ## TODO: HTML4 |shape|
2661 wakaba 1.1 media => $HTMLMQAttrChecker,
2662 wakaba 1.70 ## TODO: HTML4/XHTML1 |name|
2663 wakaba 1.1 hreflang => $HTMLLanguageTagAttrChecker,
2664     type => $HTMLIMTAttrChecker,
2665     }->{$attr_ln};
2666     if ($checker) {
2667     $attr{$attr_ln} = $attr;
2668 wakaba 1.73 } elsif ($attr_ln =~ /^data-/) {
2669     $checker = $HTMLDatasetAttrChecker;
2670     $status = $HTMLDatasetAttrStatus;
2671 wakaba 1.1 } else {
2672     $checker = $HTMLAttrChecker->{$attr_ln};
2673     }
2674     }
2675     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
2676     || $AttrChecker->{$attr_ns}->{''};
2677 wakaba 1.82 $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
2678     || $AttrStatus->{$attr_ns}->{''};
2679     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
2680 wakaba 1.62
2681 wakaba 1.1 if ($checker) {
2682 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
2683 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
2684 wakaba 1.54 #
2685 wakaba 1.1 } else {
2686     $self->{onerror}->(node => $attr, level => 'unsupported',
2687     type => 'attribute');
2688 wakaba 1.50 ## ISSUE: No conformance createria for unknown attributes in the spec
2689 wakaba 1.1 }
2690 wakaba 1.49
2691 wakaba 1.82 $self->_attr_status_info ($attr, $status);
2692 wakaba 1.1 }
2693    
2694 wakaba 1.40 $element_state->{in_a_href_original} = $self->{flag}->{in_a_href};
2695 wakaba 1.4 if (defined $attr{href}) {
2696     $self->{has_hyperlink_element} = 1;
2697 wakaba 1.40 $self->{flag}->{in_a_href} = 1;
2698 wakaba 1.4 } else {
2699 wakaba 1.1 for (qw/target ping rel media hreflang type/) {
2700     if (defined $attr{$_}) {
2701     $self->{onerror}->(node => $attr{$_},
2702     type => 'attribute not allowed');
2703     }
2704     }
2705     }
2706 wakaba 1.66
2707     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
2708 wakaba 1.1 },
2709 wakaba 1.40 check_start => sub {
2710     my ($self, $item, $element_state) = @_;
2711     $self->_add_minus_elements ($element_state, $HTMLInteractiveContent);
2712 wakaba 1.79
2713     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2714     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2715 wakaba 1.40 },
2716     check_end => sub {
2717     my ($self, $item, $element_state) = @_;
2718     $self->_remove_minus_elements ($element_state);
2719 wakaba 1.59 delete $self->{flag}->{in_a_href}
2720     unless $element_state->{in_a_href_original};
2721 wakaba 1.1
2722 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
2723 wakaba 1.1 },
2724     };
2725    
2726     $Element->{$HTML_NS}->{q} = {
2727 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2728 wakaba 1.40 %HTMLPhrasingContentChecker,
2729     check_attrs => $GetHTMLAttrsChecker->({
2730 wakaba 1.50 cite => $HTMLURIAttrChecker,
2731     }, {
2732 wakaba 1.49 %HTMLAttrStatus,
2733 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2734     cite => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2735 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2736 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
2737     sdasuff => FEATURE_HTML2X_RFC,
2738 wakaba 1.1 }),
2739 wakaba 1.66 check_start => sub {
2740     my ($self, $item, $element_state) = @_;
2741    
2742     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
2743 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2744     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2745 wakaba 1.66 },
2746 wakaba 1.1 };
2747 wakaba 1.75 ## TODO: "Quotation punctuation (such as quotation marks), if any, must be
2748     ## placed inside the <code>q</code> element." Though we cannot test the
2749     ## element against this requirement since it incluides a semantic bit,
2750     ## it might be possible to inform of the existence of quotation marks OUTSIDE
2751     ## the |q| element.
2752 wakaba 1.1
2753     $Element->{$HTML_NS}->{cite} = {
2754 wakaba 1.40 %HTMLPhrasingContentChecker,
2755 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2756 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2757     %HTMLAttrStatus,
2758 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2759 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2760 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2761 wakaba 1.49 }),
2762 wakaba 1.1 };
2763    
2764     $Element->{$HTML_NS}->{em} = {
2765 wakaba 1.40 %HTMLPhrasingContentChecker,
2766 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2767 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2768     %HTMLAttrStatus,
2769 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2770 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2771 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2772 wakaba 1.49 }),
2773 wakaba 1.1 };
2774    
2775     $Element->{$HTML_NS}->{strong} = {
2776 wakaba 1.40 %HTMLPhrasingContentChecker,
2777 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2778 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2779     %HTMLAttrStatus,
2780 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2781 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2782 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2783 wakaba 1.49 }),
2784 wakaba 1.1 };
2785    
2786     $Element->{$HTML_NS}->{small} = {
2787 wakaba 1.40 %HTMLPhrasingContentChecker,
2788 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2789     check_attrs => $GetHTMLAttrsChecker->({}, {
2790     %HTMLAttrStatus,
2791     %HTMLM12NCommonAttrStatus,
2792 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2793 wakaba 1.49 }),
2794 wakaba 1.1 };
2795    
2796 wakaba 1.51 $Element->{$HTML_NS}->{big} = {
2797     %HTMLPhrasingContentChecker,
2798     status => FEATURE_M12N10_REC,
2799     check_attrs => $GetHTMLAttrsChecker->({}, {
2800     %HTMLAttrStatus,
2801     %HTMLM12NCommonAttrStatus,
2802     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2803     }),
2804     };
2805    
2806 wakaba 1.38 $Element->{$HTML_NS}->{mark} = {
2807 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2808 wakaba 1.40 %HTMLPhrasingContentChecker,
2809 wakaba 1.1 };
2810    
2811     $Element->{$HTML_NS}->{dfn} = {
2812 wakaba 1.40 %HTMLPhrasingContentChecker,
2813 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2814 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2815     %HTMLAttrStatus,
2816 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2817 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2818 wakaba 1.49 }),
2819 wakaba 1.40 check_start => sub {
2820     my ($self, $item, $element_state) = @_;
2821     $self->_add_minus_elements ($element_state, {$HTML_NS => {dfn => 1}});
2822 wakaba 1.1
2823 wakaba 1.40 my $node = $item->{node};
2824 wakaba 1.1 my $term = $node->get_attribute_ns (undef, 'title');
2825     unless (defined $term) {
2826     for my $child (@{$node->child_nodes}) {
2827     if ($child->node_type == 1) { # ELEMENT_NODE
2828     if (defined $term) {
2829     undef $term;
2830     last;
2831     } elsif ($child->manakai_local_name eq 'abbr') {
2832     my $nsuri = $child->namespace_uri;
2833     if (defined $nsuri and $nsuri eq $HTML_NS) {
2834     my $attr = $child->get_attribute_node_ns (undef, 'title');
2835     if ($attr) {
2836     $term = $attr->value;
2837     }
2838     }
2839     }
2840     } elsif ($child->node_type == 3 or $child->node_type == 4) {
2841     ## TEXT_NODE or CDATA_SECTION_NODE
2842     if ($child->data =~ /\A[\x09-\x0D\x20]+\z/) { # Inter-element whitespace
2843     next;
2844     }
2845     undef $term;
2846     last;
2847     }
2848     }
2849     unless (defined $term) {
2850     $term = $node->text_content;
2851     }
2852     }
2853     if ($self->{term}->{$term}) {
2854     push @{$self->{term}->{$term}}, $node;
2855     } else {
2856     $self->{term}->{$term} = [$node];
2857     }
2858 wakaba 1.77 ## ISSUE: The HTML5 definition for the defined term does not work with
2859     ## |ruby| unless |dfn| has |title|.
2860 wakaba 1.79
2861     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2862     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2863 wakaba 1.40 },
2864     check_end => sub {
2865     my ($self, $item, $element_state) = @_;
2866     $self->_remove_minus_elements ($element_state);
2867 wakaba 1.1
2868 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
2869 wakaba 1.1 },
2870     };
2871    
2872     $Element->{$HTML_NS}->{abbr} = {
2873 wakaba 1.40 %HTMLPhrasingContentChecker,
2874 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2875 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2876     %HTMLAttrStatus,
2877 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2878     full => FEATURE_XHTML2_ED,
2879 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2880 wakaba 1.49 }),
2881 wakaba 1.77 ## NOTE: "If an abbreviation is pluralised, the expansion's grammatical
2882     ## number (plural vs singular) must match the grammatical number of the
2883     ## contents of the element." Though this can be checked by machine,
2884     ## it requires language-specific knowledge and dictionary, such that
2885     ## we don't support the check of the requirement.
2886     ## ISSUE: Is <abbr title="Cascading Style Sheets">CSS</abbr> conforming?
2887 wakaba 1.49 };
2888    
2889     $Element->{$HTML_NS}->{acronym} = {
2890     %HTMLPhrasingContentChecker,
2891     status => FEATURE_M12N10_REC,
2892     check_attrs => $GetHTMLAttrsChecker->({}, {
2893     %HTMLAttrStatus,
2894     %HTMLM12NCommonAttrStatus,
2895 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2896 wakaba 1.49 }),
2897 wakaba 1.1 };
2898    
2899     $Element->{$HTML_NS}->{time} = {
2900 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2901 wakaba 1.40 %HTMLPhrasingContentChecker,
2902     check_attrs => $GetHTMLAttrsChecker->({
2903 wakaba 1.1 datetime => sub { 1 }, # checked in |checker|
2904 wakaba 1.49 }, {
2905     %HTMLAttrStatus,
2906     %HTMLM12NCommonAttrStatus,
2907 wakaba 1.72 datetime => FEATURE_HTML5_FD,
2908 wakaba 1.1 }),
2909     ## TODO: Write tests
2910 wakaba 1.40 check_end => sub {
2911     my ($self, $item, $element_state) = @_;
2912 wakaba 1.1
2913 wakaba 1.40 my $attr = $item->{node}->get_attribute_node_ns (undef, 'datetime');
2914 wakaba 1.1 my $input;
2915     my $reg_sp;
2916     my $input_node;
2917     if ($attr) {
2918     $input = $attr->value;
2919     $reg_sp = qr/[\x09-\x0D\x20]*/;
2920     $input_node = $attr;
2921     } else {
2922 wakaba 1.40 $input = $item->{node}->text_content;
2923 wakaba 1.1 $reg_sp = qr/\p{Zs}*/;
2924 wakaba 1.40 $input_node = $item->{node};
2925 wakaba 1.1
2926     ## ISSUE: What is the definition for "successfully extracts a date
2927     ## or time"? If the algorithm says the string is invalid but
2928     ## return some date or time, is it "successfully"?
2929     }
2930    
2931     my $hour;
2932     my $minute;
2933     my $second;
2934     if ($input =~ /
2935     \A
2936     [\x09-\x0D\x20]*
2937     ([0-9]+) # 1
2938     (?>
2939     -([0-9]+) # 2
2940     -([0-9]+) # 3
2941     [\x09-\x0D\x20]*
2942     (?>
2943     T
2944     [\x09-\x0D\x20]*
2945     )?
2946     ([0-9]+) # 4
2947     :([0-9]+) # 5
2948     (?>
2949     :([0-9]+(?>\.[0-9]*)?|\.[0-9]*) # 6
2950     )?
2951     [\x09-\x0D\x20]*
2952     (?>
2953     Z
2954     [\x09-\x0D\x20]*
2955     |
2956     [+-]([0-9]+):([0-9]+) # 7, 8
2957     [\x09-\x0D\x20]*
2958     )?
2959     \z
2960     |
2961     :([0-9]+) # 9
2962     (?>
2963     :([0-9]+(?>\.[0-9]*)?|\.[0-9]*) # 10
2964     )?
2965     [\x09-\x0D\x20]*\z
2966     )
2967     /x) {
2968     if (defined $2) { ## YYYY-MM-DD T? hh:mm
2969     if (length $1 != 4 or length $2 != 2 or length $3 != 2 or
2970     length $4 != 2 or length $5 != 2) {
2971     $self->{onerror}->(node => $input_node,
2972     type => 'dateortime:syntax error');
2973     }
2974    
2975     if (1 <= $2 and $2 <= 12) {
2976     $self->{onerror}->(node => $input_node, type => 'datetime:bad day')
2977     if $3 < 1 or
2978     $3 > [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]->[$2];
2979     $self->{onerror}->(node => $input_node, type => 'datetime:bad day')
2980     if $2 == 2 and $3 == 29 and
2981     not ($1 % 400 == 0 or ($1 % 4 == 0 and $1 % 100 != 0));
2982     } else {
2983     $self->{onerror}->(node => $input_node,
2984     type => 'datetime:bad month');
2985     }
2986    
2987     ($hour, $minute, $second) = ($4, $5, $6);
2988    
2989     if (defined $7) { ## [+-]hh:mm
2990     if (length $7 != 2 or length $8 != 2) {
2991     $self->{onerror}->(node => $input_node,
2992     type => 'dateortime:syntax error');
2993     }
2994    
2995     $self->{onerror}->(node => $input_node,
2996     type => 'datetime:bad timezone hour')
2997     if $7 > 23;
2998     $self->{onerror}->(node => $input_node,
2999     type => 'datetime:bad timezone minute')
3000     if $8 > 59;
3001     }
3002     } else { ## hh:mm
3003     if (length $1 != 2 or length $9 != 2) {
3004     $self->{onerror}->(node => $input_node,
3005     type => qq'dateortime:syntax error');
3006     }
3007    
3008     ($hour, $minute, $second) = ($1, $9, $10);
3009     }
3010    
3011     $self->{onerror}->(node => $input_node, type => 'datetime:bad hour')
3012     if $hour > 23;
3013     $self->{onerror}->(node => $input_node, type => 'datetime:bad minute')
3014     if $minute > 59;
3015    
3016     if (defined $second) { ## s
3017     ## NOTE: Integer part of second don't have to have length of two.
3018    
3019     if (substr ($second, 0, 1) eq '.') {
3020     $self->{onerror}->(node => $input_node,
3021     type => 'dateortime:syntax error');
3022     }
3023    
3024     $self->{onerror}->(node => $input_node, type => 'datetime:bad second')
3025     if $second >= 60;
3026     }
3027     } else {
3028     $self->{onerror}->(node => $input_node,
3029     type => 'dateortime:syntax error');
3030     }
3031    
3032 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
3033 wakaba 1.1 },
3034     };
3035    
3036     $Element->{$HTML_NS}->{meter} = { ## TODO: "The recommended way of giving the value is to include it as contents of the element"
3037 wakaba 1.77 ## TODO: value inequalities (HTML5 revision 1463)
3038 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3039 wakaba 1.40 %HTMLPhrasingContentChecker,
3040     check_attrs => $GetHTMLAttrsChecker->({
3041 wakaba 1.1 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3042     min => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3043     low => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3044     high => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3045     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3046     optimum => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3047 wakaba 1.50 }, {
3048     %HTMLAttrStatus,
3049     high => FEATURE_HTML5_DEFAULT,
3050     low => FEATURE_HTML5_DEFAULT,
3051     max => FEATURE_HTML5_DEFAULT,
3052     min => FEATURE_HTML5_DEFAULT,
3053     optimum => FEATURE_HTML5_DEFAULT,
3054     value => FEATURE_HTML5_DEFAULT,
3055 wakaba 1.1 }),
3056     };
3057    
3058     $Element->{$HTML_NS}->{progress} = { ## TODO: recommended to use content
3059 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3060 wakaba 1.40 %HTMLPhrasingContentChecker,
3061     check_attrs => $GetHTMLAttrsChecker->({
3062 wakaba 1.1 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift >= 0 }),
3063     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift > 0 }),
3064 wakaba 1.50 }, {
3065     %HTMLAttrStatus,
3066     max => FEATURE_HTML5_DEFAULT,
3067     value => FEATURE_HTML5_DEFAULT,
3068 wakaba 1.1 }),
3069     };
3070    
3071     $Element->{$HTML_NS}->{code} = {
3072 wakaba 1.40 %HTMLPhrasingContentChecker,
3073 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3074 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3075     %HTMLAttrStatus,
3076 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3077 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3078 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3079 wakaba 1.49 }),
3080 wakaba 1.1 };
3081    
3082     $Element->{$HTML_NS}->{var} = {
3083 wakaba 1.40 %HTMLPhrasingContentChecker,
3084 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3085 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3086     %HTMLAttrStatus,
3087 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3088 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3089 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3090 wakaba 1.49 }),
3091 wakaba 1.1 };
3092    
3093     $Element->{$HTML_NS}->{samp} = {
3094 wakaba 1.40 %HTMLPhrasingContentChecker,
3095 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3096 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3097     %HTMLAttrStatus,
3098 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3099 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3100 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3101 wakaba 1.49 }),
3102 wakaba 1.1 };
3103    
3104     $Element->{$HTML_NS}->{kbd} = {
3105 wakaba 1.40 %HTMLPhrasingContentChecker,
3106 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3107 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3108     %HTMLAttrStatus,
3109 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3110 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3111 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3112 wakaba 1.49 }),
3113 wakaba 1.1 };
3114    
3115     $Element->{$HTML_NS}->{sub} = {
3116 wakaba 1.40 %HTMLPhrasingContentChecker,
3117 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3118 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3119     %HTMLAttrStatus,
3120 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3121 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3122 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
3123 wakaba 1.49 }),
3124 wakaba 1.1 };
3125    
3126 wakaba 1.51 $Element->{$HTML_NS}->{sup} = $Element->{$HTML_NS}->{sub};
3127 wakaba 1.1
3128     $Element->{$HTML_NS}->{span} = {
3129 wakaba 1.40 %HTMLPhrasingContentChecker,
3130 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3131 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3132     %HTMLAttrStatus,
3133 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3134 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
3135     dataformatas => FEATURE_HTML4_REC_RESERVED,
3136     datasrc => FEATURE_HTML4_REC_RESERVED,
3137 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3138 wakaba 1.61 sdaform => FEATURE_HTML2X_RFC,
3139 wakaba 1.49 }),
3140 wakaba 1.1 };
3141    
3142     $Element->{$HTML_NS}->{i} = {
3143 wakaba 1.40 %HTMLPhrasingContentChecker,
3144 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3145     check_attrs => $GetHTMLAttrsChecker->({}, {
3146     %HTMLAttrStatus,
3147     %HTMLM12NCommonAttrStatus,
3148 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3149 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3150 wakaba 1.49 }),
3151 wakaba 1.1 };
3152    
3153 wakaba 1.51 $Element->{$HTML_NS}->{b} = $Element->{$HTML_NS}->{i};
3154    
3155 wakaba 1.61 $Element->{$HTML_NS}->{tt} = {
3156     %HTMLPhrasingContentChecker,
3157     status => FEATURE_M12N10_REC,
3158     check_attrs => $GetHTMLAttrsChecker->({}, {
3159     %HTMLAttrStatus,
3160     %HTMLM12NCommonAttrStatus,
3161     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3162     sdaform => FEATURE_HTML20_RFC,
3163     }),
3164     };
3165 wakaba 1.51
3166     $Element->{$HTML_NS}->{s} = {
3167 wakaba 1.40 %HTMLPhrasingContentChecker,
3168 wakaba 1.51 status => FEATURE_M12N10_REC_DEPRECATED,
3169 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3170     %HTMLAttrStatus,
3171     %HTMLM12NCommonAttrStatus,
3172 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3173 wakaba 1.49 }),
3174 wakaba 1.1 };
3175    
3176 wakaba 1.51 $Element->{$HTML_NS}->{strike} = $Element->{$HTML_NS}->{s};
3177    
3178     $Element->{$HTML_NS}->{u} = $Element->{$HTML_NS}->{s};
3179    
3180 wakaba 1.1 $Element->{$HTML_NS}->{bdo} = {
3181 wakaba 1.40 %HTMLPhrasingContentChecker,
3182 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3183 wakaba 1.40 check_attrs => sub {
3184     my ($self, $item, $element_state) = @_;
3185 wakaba 1.49 $GetHTMLAttrsChecker->({}, {
3186     %HTMLAttrStatus,
3187 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3188     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3189     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3190 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3191 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3192     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3193 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
3194     sdasuff => FEATURE_HTML2X_RFC,
3195 wakaba 1.49 })->($self, $item, $element_state);
3196 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'dir')) {
3197     $self->{onerror}->(node => $item->{node},
3198     type => 'attribute missing:dir');
3199 wakaba 1.1 }
3200     },
3201     ## ISSUE: The spec does not directly say that |dir| is a enumerated attr.
3202     };
3203    
3204 wakaba 1.29 =pod
3205    
3206     ## TODO:
3207    
3208     +
3209     + <p>Partly because of the confusion described above, authors are
3210     + strongly recommended to always mark up all paragraphs with the
3211     + <code>p</code> element, and to not have any <code>ins</code> or
3212     + <code>del</code> elements that cross across any <span
3213     + title="paragraph">implied paragraphs</span>.</p>
3214     +
3215     (An informative note)
3216    
3217     <p><code>ins</code> elements should not cross <span
3218     + title="paragraph">implied paragraph</span> boundaries.</p>
3219     (normative)
3220    
3221     + <p><code>del</code> elements should not cross <span
3222     + title="paragraph">implied paragraph</span> boundaries.</p>
3223     (normative)
3224    
3225     =cut
3226    
3227 wakaba 1.1 $Element->{$HTML_NS}->{ins} = {
3228 wakaba 1.40 %HTMLTransparentChecker,
3229 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3230 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3231 wakaba 1.1 cite => $HTMLURIAttrChecker,
3232     datetime => $HTMLDatetimeAttrChecker,
3233 wakaba 1.49 }, {
3234     %HTMLAttrStatus,
3235     %HTMLM12NCommonAttrStatus,
3236 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3237 wakaba 1.72 datetime => FEATURE_HTML5_FD | FEATURE_M12N10_REC,
3238 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3239 wakaba 1.1 }),
3240 wakaba 1.66 check_start => sub {
3241     my ($self, $item, $element_state) = @_;
3242    
3243     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
3244 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3245     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3246 wakaba 1.66 },
3247 wakaba 1.1 };
3248    
3249     $Element->{$HTML_NS}->{del} = {
3250 wakaba 1.40 %HTMLTransparentChecker,
3251 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3252 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3253 wakaba 1.1 cite => $HTMLURIAttrChecker,
3254     datetime => $HTMLDatetimeAttrChecker,
3255 wakaba 1.49 }, {
3256     %HTMLAttrStatus,
3257     %HTMLM12NCommonAttrStatus,
3258 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3259 wakaba 1.72 datetime => FEATURE_HTML5_FD | FEATURE_M12N10_REC,
3260 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3261 wakaba 1.1 }),
3262 wakaba 1.40 check_end => sub {
3263     my ($self, $item, $element_state) = @_;
3264     if ($element_state->{has_significant}) {
3265     ## NOTE: Significantness flag does not propagate.
3266     } elsif ($item->{transparent}) {
3267     #
3268     } else {
3269     $self->{onerror}->(node => $item->{node},
3270     level => $self->{should_level},
3271     type => 'no significant content');
3272     }
3273 wakaba 1.1 },
3274 wakaba 1.66 check_start => sub {
3275     my ($self, $item, $element_state) = @_;
3276    
3277     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
3278 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3279     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3280 wakaba 1.66 },
3281 wakaba 1.1 };
3282    
3283 wakaba 1.35 $Element->{$HTML_NS}->{figure} = {
3284 wakaba 1.72 %HTMLFlowContentChecker,
3285 wakaba 1.48 status => FEATURE_HTML5_FD,
3286 wakaba 1.72 ## NOTE: legend, Flow | Flow, legend?
3287 wakaba 1.41 check_child_element => sub {
3288     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3289     $child_is_transparent, $element_state) = @_;
3290     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3291     $self->{onerror}->(node => $child_el,
3292     type => 'element not allowed:minus',
3293     level => $self->{must_level});
3294     $element_state->{has_non_legend} = 1;
3295     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3296     #
3297     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') {
3298     if ($element_state->{has_legend_at_first}) {
3299     $self->{onerror}->(node => $child_el,
3300     type => 'element not allowed:figure legend',
3301     level => $self->{must_level});
3302     } elsif ($element_state->{has_legend}) {
3303     $self->{onerror}->(node => $element_state->{has_legend},
3304     type => 'element not allowed:figure legend',
3305     level => $self->{must_level});
3306     $element_state->{has_legend} = $child_el;
3307     } elsif ($element_state->{has_non_legend}) {
3308     $element_state->{has_legend} = $child_el;
3309     } else {
3310     $element_state->{has_legend_at_first} = 1;
3311 wakaba 1.35 }
3312 wakaba 1.41 delete $element_state->{has_non_legend};
3313     } else {
3314 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
3315 wakaba 1.43 $element_state->{has_non_legend} = 1 unless $child_is_transparent;
3316 wakaba 1.41 }
3317     },
3318     check_child_text => sub {
3319     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3320     if ($has_significant) {
3321     $element_state->{has_non_legend} = 1;
3322 wakaba 1.35 }
3323 wakaba 1.41 },
3324     check_end => sub {
3325     my ($self, $item, $element_state) = @_;
3326 wakaba 1.35
3327 wakaba 1.41 if ($element_state->{has_legend_at_first}) {
3328     #
3329     } elsif ($element_state->{has_legend}) {
3330     if ($element_state->{has_non_legend}) {
3331     $self->{onerror}->(node => $element_state->{has_legend},
3332 wakaba 1.35 type => 'element not allowed:figure legend',
3333     level => $self->{must_level});
3334     }
3335     }
3336 wakaba 1.41
3337 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
3338 wakaba 1.41 ## ISSUE: |<figure><legend>aa</legend></figure>| should be an error?
3339 wakaba 1.35 },
3340     };
3341 wakaba 1.8 ## TODO: Test for <nest/> in <figure/>
3342 wakaba 1.1
3343 wakaba 1.92 my $AttrCheckerNotImplemented = sub {
3344     my ($self, $attr) = @_;
3345     $self->{onerror}->(node => $attr, level => 'unsupported',
3346     type => 'attribute');
3347     };
3348    
3349 wakaba 1.1 $Element->{$HTML_NS}->{img} = {
3350 wakaba 1.40 %HTMLEmptyChecker,
3351 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3352 wakaba 1.40 check_attrs => sub {
3353     my ($self, $item, $element_state) = @_;
3354 wakaba 1.1 $GetHTMLAttrsChecker->({
3355 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
3356     bottom => 1, middle => 1, top => 1, left => 1, right => 1,
3357     }),
3358 wakaba 1.1 alt => sub { }, ## NOTE: No syntactical requirement
3359 wakaba 1.70 border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3360 wakaba 1.1 src => $HTMLURIAttrChecker,
3361     usemap => $HTMLUsemapAttrChecker,
3362 wakaba 1.70 hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3363 wakaba 1.1 ismap => sub {
3364 wakaba 1.40 my ($self, $attr, $parent_item) = @_;
3365     if (not $self->{flag}->{in_a_href}) {
3366 wakaba 1.15 $self->{onerror}->(node => $attr,
3367 wakaba 1.59 type => 'attribute not allowed:ismap',
3368     level => $self->{must_level});
3369 wakaba 1.1 }
3370 wakaba 1.40 $GetHTMLBooleanAttrChecker->('ismap')->($self, $attr, $parent_item);
3371 wakaba 1.1 },
3372 wakaba 1.70 longdesc => $HTMLURIAttrChecker,
3373     ## TODO: HTML4 |name|
3374 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3375 wakaba 1.70 vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3376 wakaba 1.92 width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3377 wakaba 1.49 }, {
3378     %HTMLAttrStatus,
3379 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3380 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
3381 wakaba 1.50 alt => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3382 wakaba 1.49 border => FEATURE_M12N10_REC_DEPRECATED,
3383 wakaba 1.50 height => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3384 wakaba 1.49 hspace => FEATURE_M12N10_REC_DEPRECATED,
3385 wakaba 1.82 ismap => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3386 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3387 wakaba 1.49 longdesc => FEATURE_M12N10_REC,
3388     name => FEATURE_M12N10_REC_DEPRECATED,
3389 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
3390 wakaba 1.82 src => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3391     usemap => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3392 wakaba 1.49 vspace => FEATURE_M12N10_REC_DEPRECATED,
3393 wakaba 1.50 width => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3394 wakaba 1.66 })->($self, $item, $element_state);
3395 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'alt')) {
3396     $self->{onerror}->(node => $item->{node},
3397 wakaba 1.37 type => 'attribute missing:alt',
3398     level => $self->{should_level});
3399 wakaba 1.1 }
3400 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'src')) {
3401     $self->{onerror}->(node => $item->{node},
3402     type => 'attribute missing:src');
3403 wakaba 1.1 }
3404 wakaba 1.66
3405     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
3406     $element_state->{uri_info}->{lowsrc}->{type}->{embedded} = 1;
3407     $element_state->{uri_info}->{dynsrc}->{type}->{embedded} = 1;
3408     $element_state->{uri_info}->{longdesc}->{type}->{cite} = 1;
3409 wakaba 1.1 },
3410     };
3411    
3412     $Element->{$HTML_NS}->{iframe} = {
3413 wakaba 1.40 %HTMLTextChecker,
3414 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3415     ## NOTE: Not part of M12N10 Strict
3416 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3417 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3418 wakaba 1.76 name => $HTMLBrowsingContextNameAttrChecker,
3419 wakaba 1.92 sandbox => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->({
3420     'allow-same-origin' => 1, 'allow-forms' => 1, 'allow-scripts' => 1,
3421     }),
3422     seemless => $GetHTMLBooleanAttrChecker->('seemless'),
3423 wakaba 1.1 src => $HTMLURIAttrChecker,
3424 wakaba 1.92 width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3425 wakaba 1.49 }, {
3426     %HTMLAttrStatus,
3427     %HTMLM12NCommonAttrStatus,
3428     align => FEATURE_XHTML10_REC,
3429 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3430 wakaba 1.49 frameborder => FEATURE_M12N10_REC,
3431 wakaba 1.92 height => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3432 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3433 wakaba 1.49 longdesc => FEATURE_M12N10_REC,
3434     marginheight => FEATURE_M12N10_REC,
3435     marginwidth => FEATURE_M12N10_REC,
3436 wakaba 1.76 #name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
3437     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3438 wakaba 1.92 sandbox => FEATURE_HTML5_DEFAULT,
3439 wakaba 1.49 scrolling => FEATURE_M12N10_REC,
3440 wakaba 1.92 seemless => FEATURE_HTML5_DEFAULT,
3441 wakaba 1.50 src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3442     title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3443 wakaba 1.92 width => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3444 wakaba 1.1 }),
3445 wakaba 1.66 check_start => sub {
3446     my ($self, $item, $element_state) = @_;
3447    
3448     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
3449 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3450     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3451 wakaba 1.66 },
3452 wakaba 1.40 };
3453    
3454 wakaba 1.1 $Element->{$HTML_NS}->{embed} = {
3455 wakaba 1.40 %HTMLEmptyChecker,
3456 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3457 wakaba 1.40 check_attrs => sub {
3458     my ($self, $item, $element_state) = @_;
3459 wakaba 1.1 my $has_src;
3460 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
3461 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
3462     $attr_ns = '' unless defined $attr_ns;
3463     my $attr_ln = $attr->manakai_local_name;
3464     my $checker;
3465 wakaba 1.73
3466     my $status = {
3467     %HTMLAttrStatus,
3468     height => FEATURE_HTML5_DEFAULT,
3469     src => FEATURE_HTML5_DEFAULT,
3470     type => FEATURE_HTML5_DEFAULT,
3471     width => FEATURE_HTML5_DEFAULT,
3472     }->{$attr_ln};
3473    
3474 wakaba 1.1 if ($attr_ns eq '') {
3475     if ($attr_ln eq 'src') {
3476     $checker = $HTMLURIAttrChecker;
3477     $has_src = 1;
3478     } elsif ($attr_ln eq 'type') {
3479     $checker = $HTMLIMTAttrChecker;
3480 wakaba 1.92 } elsif ($attr_ln eq 'width' or $attr_ln eq 'height') {
3481     $checker = $AttrCheckerNotImplemented; ## TODO: because spec does not define them yet.
3482 wakaba 1.73 } elsif ($attr_ln =~ /^data-/) {
3483     $checker = $HTMLDatasetAttrChecker;
3484     $status = $HTMLDatasetAttrStatus;
3485 wakaba 1.1 } else {
3486     $checker = $HTMLAttrChecker->{$attr_ln}
3487     || sub { }; ## NOTE: Any local attribute is ok.
3488 wakaba 1.82 $status = FEATURE_HTML5_DEFAULT | FEATURE_ALLOWED;
3489 wakaba 1.1 }
3490     }
3491     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
3492 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
3493     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
3494     || $AttrStatus->{$attr_ns}->{''};
3495     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
3496 wakaba 1.62
3497 wakaba 1.1 if ($checker) {
3498 wakaba 1.66 $checker->($self, $attr, $item, $element_state);
3499 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
3500 wakaba 1.54 #
3501 wakaba 1.1 } else {
3502     $self->{onerror}->(node => $attr, level => 'unsupported',
3503     type => 'attribute');
3504 wakaba 1.50 ## ISSUE: No conformance createria for global attributes in the spec
3505     }
3506    
3507 wakaba 1.82 $self->_attr_status_info ($attr, $status);
3508 wakaba 1.1 }
3509    
3510     unless ($has_src) {
3511 wakaba 1.40 $self->{onerror}->(node => $item->{node},
3512 wakaba 1.1 type => 'attribute missing:src');
3513     }
3514 wakaba 1.66
3515     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
3516 wakaba 1.1 },
3517     };
3518    
3519 wakaba 1.49 ## TODO:
3520     ## {applet} FEATURE_M12N10_REC_DEPRECATED
3521     ## class, id, title, alt, archive, code, codebase, height, object, width name style,hspace,vspace(xhtml10)
3522    
3523 wakaba 1.1 $Element->{$HTML_NS}->{object} = {
3524 wakaba 1.40 %HTMLTransparentChecker,
3525 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3526 wakaba 1.40 check_attrs => sub {
3527     my ($self, $item, $element_state) = @_;
3528 wakaba 1.1 $GetHTMLAttrsChecker->({
3529 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
3530     bottom => 1, middle => 1, top => 1, left => 1, right => 1,
3531     }),
3532     archive => $HTMLSpaceURIsAttrChecker,
3533     ## TODO: Relative to @codebase
3534     border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3535     classid => $HTMLURIAttrChecker,
3536     codebase => $HTMLURIAttrChecker,
3537     codetype => $HTMLIMTAttrChecker,
3538     ## TODO: "RECOMMENDED when |classid| is specified" [HTML4]
3539 wakaba 1.1 data => $HTMLURIAttrChecker,
3540 wakaba 1.70 declare => $GetHTMLBooleanAttrChecker->('declare'),
3541     ## NOTE: "The object MUST be instantiated by a subsequent OBJECT ..."
3542     ## [HTML4] but we don't know how to test this.
3543     hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3544 wakaba 1.76 name => $HTMLBrowsingContextNameAttrChecker,
3545 wakaba 1.70 standby => sub {}, ## NOTE: %Text; in HTML4
3546 wakaba 1.1 type => $HTMLIMTAttrChecker,
3547     usemap => $HTMLUsemapAttrChecker,
3548 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3549 wakaba 1.70 vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3550 wakaba 1.92 width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3551 wakaba 1.49 }, {
3552     %HTMLAttrStatus,
3553 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3554 wakaba 1.49 align => FEATURE_XHTML10_REC,
3555 wakaba 1.82 archive => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3556 wakaba 1.49 border => FEATURE_XHTML10_REC,
3557     classid => FEATURE_M12N10_REC,
3558     codebase => FEATURE_M12N10_REC,
3559     codetype => FEATURE_M12N10_REC,
3560 wakaba 1.82 'content-length' => FEATURE_XHTML2_ED,
3561 wakaba 1.50 data => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3562 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
3563     dataformatas => FEATURE_HTML4_REC_RESERVED,
3564     datasrc => FEATURE_HTML4_REC_RESERVED,
3565 wakaba 1.82 declare => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3566 wakaba 1.50 height => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3567 wakaba 1.49 hspace => FEATURE_XHTML10_REC,
3568 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3569 wakaba 1.76 name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3570 wakaba 1.49 standby => FEATURE_M12N10_REC,
3571 wakaba 1.50 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3572     type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3573     usemap => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3574 wakaba 1.49 vspace => FEATURE_XHTML10_REC,
3575 wakaba 1.50 width => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3576 wakaba 1.66 })->($self, $item, $element_state);
3577 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'data')) {
3578     unless ($item->{node}->has_attribute_ns (undef, 'type')) {
3579     $self->{onerror}->(node => $item->{node},
3580 wakaba 1.1 type => 'attribute missing:data|type');
3581     }
3582     }
3583 wakaba 1.66
3584     $element_state->{uri_info}->{data}->{type}->{embedded} = 1;
3585     $element_state->{uri_info}->{classid}->{type}->{embedded} = 1;
3586     $element_state->{uri_info}->{codebase}->{type}->{base} = 1;
3587     ## TODO: archive
3588     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
3589 wakaba 1.1 },
3590 wakaba 1.72 ## NOTE: param*, transparent (Flow)
3591 wakaba 1.41 check_child_element => sub {
3592     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3593     $child_is_transparent, $element_state) = @_;
3594     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3595     $self->{onerror}->(node => $child_el,
3596     type => 'element not allowed:minus',
3597     level => $self->{must_level});
3598     $element_state->{has_non_legend} = 1;
3599     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3600     #
3601     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'param') {
3602     if ($element_state->{has_non_param}) {
3603 wakaba 1.72 $self->{onerror}->(node => $child_el, ## TODO: type
3604     type => 'element not allowed:flow',
3605 wakaba 1.41 level => $self->{must_level});
3606 wakaba 1.39 }
3607 wakaba 1.41 } else {
3608 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
3609 wakaba 1.41 $element_state->{has_non_param} = 1;
3610 wakaba 1.39 }
3611 wakaba 1.25 },
3612 wakaba 1.41 check_child_text => sub {
3613     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3614     if ($has_significant) {
3615     $element_state->{has_non_param} = 1;
3616     }
3617 wakaba 1.42 },
3618     check_end => sub {
3619     my ($self, $item, $element_state) = @_;
3620     if ($element_state->{has_significant}) {
3621 wakaba 1.46 $item->{real_parent_state}->{has_significant} = 1;
3622 wakaba 1.42 } elsif ($item->{node}->manakai_parent_element) {
3623     ## NOTE: Transparent.
3624     } else {
3625     $self->{onerror}->(node => $item->{node},
3626     level => $self->{should_level},
3627     type => 'no significant content');
3628     }
3629     },
3630 wakaba 1.8 ## TODO: Tests for <nest/> in <object/>
3631 wakaba 1.1 };
3632 wakaba 1.41 ## ISSUE: Is |<menu><object data><li>aa</li></object></menu>| conforming?
3633     ## What about |<section><object data><style scoped></style>x</object></section>|?
3634     ## |<section><ins></ins><object data><style scoped></style>x</object></section>|?
3635 wakaba 1.1
3636     $Element->{$HTML_NS}->{param} = {
3637 wakaba 1.40 %HTMLEmptyChecker,
3638 wakaba 1.94 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3639 wakaba 1.40 check_attrs => sub {
3640     my ($self, $item, $element_state) = @_;
3641 wakaba 1.1 $GetHTMLAttrsChecker->({
3642     name => sub { },
3643 wakaba 1.70 type => $HTMLIMTAttrChecker,
3644 wakaba 1.1 value => sub { },
3645 wakaba 1.70 valuetype => $GetHTMLEnumeratedAttrChecker->({
3646     data => 1, ref => 1, object => 1,
3647     }),
3648 wakaba 1.49 }, {
3649     %HTMLAttrStatus,
3650 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3651 wakaba 1.94 name => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3652 wakaba 1.82 type => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3653 wakaba 1.94 value => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3654 wakaba 1.82 valuetype => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3655 wakaba 1.66 })->(@_);
3656 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'name')) {
3657     $self->{onerror}->(node => $item->{node},
3658 wakaba 1.1 type => 'attribute missing:name');
3659     }
3660 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'value')) {
3661     $self->{onerror}->(node => $item->{node},
3662 wakaba 1.1 type => 'attribute missing:value');
3663     }
3664     },
3665     };
3666    
3667     $Element->{$HTML_NS}->{video} = {
3668 wakaba 1.40 %HTMLTransparentChecker,
3669 wakaba 1.48 status => FEATURE_HTML5_LC,
3670 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3671 wakaba 1.1 src => $HTMLURIAttrChecker,
3672     ## TODO: start, loopstart, loopend, end
3673     ## ISSUE: they MUST be "value time offset"s. Value?
3674 wakaba 1.11 ## ISSUE: playcount has no conformance creteria
3675 wakaba 1.1 autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
3676     controls => $GetHTMLBooleanAttrChecker->('controls'),
3677 wakaba 1.59 poster => $HTMLURIAttrChecker,
3678 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3679     width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3680 wakaba 1.50 }, {
3681     %HTMLAttrStatus,
3682     autoplay => FEATURE_HTML5_LC,
3683     controls => FEATURE_HTML5_LC,
3684     end => FEATURE_HTML5_LC,
3685     height => FEATURE_HTML5_LC,
3686     loopend => FEATURE_HTML5_LC,
3687     loopstart => FEATURE_HTML5_LC,
3688     playcount => FEATURE_HTML5_LC,
3689     poster => FEATURE_HTML5_LC,
3690     src => FEATURE_HTML5_LC,
3691     start => FEATURE_HTML5_LC,
3692     width => FEATURE_HTML5_LC,
3693 wakaba 1.1 }),
3694 wakaba 1.42 check_start => sub {
3695     my ($self, $item, $element_state) = @_;
3696     $element_state->{allow_source}
3697     = not $item->{node}->has_attribute_ns (undef, 'src');
3698     $element_state->{has_source} ||= $element_state->{allow_source} * -1;
3699     ## NOTE: It might be set true by |check_element|.
3700 wakaba 1.66
3701     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
3702     $element_state->{uri_info}->{poster}->{type}->{embedded} = 1;
3703 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3704     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3705 wakaba 1.42 },
3706     check_child_element => sub {
3707     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3708     $child_is_transparent, $element_state) = @_;
3709     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3710     $self->{onerror}->(node => $child_el,
3711     type => 'element not allowed:minus',
3712     level => $self->{must_level});
3713     delete $element_state->{allow_source};
3714     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3715     #
3716     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'source') {
3717 wakaba 1.45 unless ($element_state->{allow_source}) {
3718 wakaba 1.72 $self->{onerror}->(node => $child_el, ## TODO: type
3719     type => 'element not allowed:flow',
3720 wakaba 1.42 level => $self->{must_level});
3721     }
3722 wakaba 1.45 $element_state->{has_source} = 1;
3723 wakaba 1.1 } else {
3724 wakaba 1.42 delete $element_state->{allow_source};
3725 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
3726 wakaba 1.42 }
3727     },
3728     check_child_text => sub {
3729     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3730     if ($has_significant) {
3731     delete $element_state->{allow_source};
3732     }
3733 wakaba 1.72 $HTMLFlowContentChecker{check_child_text}->(@_);
3734 wakaba 1.42 },
3735     check_end => sub {
3736     my ($self, $item, $element_state) = @_;
3737     if ($element_state->{has_source} == -1) {
3738     $self->{onerror}->(node => $item->{node},
3739     type => 'element missing:source',
3740     level => $self->{must_level});
3741 wakaba 1.1 }
3742 wakaba 1.42
3743     $Element->{$HTML_NS}->{object}->{check_end}->(@_);
3744 wakaba 1.1 },
3745     };
3746    
3747     $Element->{$HTML_NS}->{audio} = {
3748 wakaba 1.40 %{$Element->{$HTML_NS}->{video}},
3749 wakaba 1.48 status => FEATURE_HTML5_LC,
3750 wakaba 1.42 check_attrs => $GetHTMLAttrsChecker->({
3751     src => $HTMLURIAttrChecker,
3752     ## TODO: start, loopstart, loopend, end
3753     ## ISSUE: they MUST be "value time offset"s. Value?
3754     ## ISSUE: playcount has no conformance creteria
3755     autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
3756     controls => $GetHTMLBooleanAttrChecker->('controls'),
3757 wakaba 1.50 }, {
3758     %HTMLAttrStatus,
3759     autoplay => FEATURE_HTML5_LC,
3760     controls => FEATURE_HTML5_LC,
3761     end => FEATURE_HTML5_LC,
3762     loopend => FEATURE_HTML5_LC,
3763     loopstart => FEATURE_HTML5_LC,
3764     playcount => FEATURE_HTML5_LC,
3765     src => FEATURE_HTML5_LC,
3766     start => FEATURE_HTML5_LC,
3767 wakaba 1.42 }),
3768 wakaba 1.1 };
3769    
3770     $Element->{$HTML_NS}->{source} = {
3771 wakaba 1.40 %HTMLEmptyChecker,
3772 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3773 wakaba 1.40 check_attrs => sub {
3774     my ($self, $item, $element_state) = @_;
3775 wakaba 1.1 $GetHTMLAttrsChecker->({
3776 wakaba 1.90 media => $HTMLMQAttrChecker,
3777     pixelratio => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3778     src => $HTMLURIAttrChecker, ## ISSUE: Negative or zero pixelratio=""
3779 wakaba 1.1 type => $HTMLIMTAttrChecker,
3780 wakaba 1.50 }, {
3781     %HTMLAttrStatus,
3782     media => FEATURE_HTML5_DEFAULT,
3783 wakaba 1.90 pixelratio => FEATURE_HTML5_DEFAULT,
3784 wakaba 1.50 src => FEATURE_HTML5_DEFAULT,
3785     type => FEATURE_HTML5_DEFAULT,
3786 wakaba 1.66 })->(@_);
3787 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'src')) {
3788     $self->{onerror}->(node => $item->{node},
3789 wakaba 1.1 type => 'attribute missing:src');
3790     }
3791 wakaba 1.66
3792     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
3793 wakaba 1.1 },
3794     };
3795    
3796     $Element->{$HTML_NS}->{canvas} = {
3797 wakaba 1.40 %HTMLTransparentChecker,
3798 wakaba 1.89 status => FEATURE_HTML5_COMPLETE,
3799 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3800 wakaba 1.1 height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3801     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3802 wakaba 1.50 }, {
3803     %HTMLAttrStatus,
3804 wakaba 1.89 height => FEATURE_HTML5_COMPLETE,
3805     width => FEATURE_HTML5_COMPLETE,
3806 wakaba 1.1 }),
3807     };
3808    
3809     $Element->{$HTML_NS}->{map} = {
3810 wakaba 1.72 %HTMLFlowContentChecker,
3811 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3812 wakaba 1.40 check_attrs => sub {
3813     my ($self, $item, $element_state) = @_;
3814 wakaba 1.4 my $has_id;
3815     $GetHTMLAttrsChecker->({
3816     id => sub {
3817     ## NOTE: same as global |id=""|, with |$self->{map}| registeration
3818     my ($self, $attr) = @_;
3819     my $value = $attr->value;
3820     if (length $value > 0) {
3821     if ($self->{id}->{$value}) {
3822     $self->{onerror}->(node => $attr, type => 'duplicate ID');
3823     push @{$self->{id}->{$value}}, $attr;
3824     } else {
3825     $self->{id}->{$value} = [$attr];
3826     }
3827 wakaba 1.1 } else {
3828 wakaba 1.4 ## NOTE: MUST contain at least one character
3829     $self->{onerror}->(node => $attr, type => 'empty attribute value');
3830 wakaba 1.1 }
3831 wakaba 1.4 if ($value =~ /[\x09-\x0D\x20]/) {
3832     $self->{onerror}->(node => $attr, type => 'space in ID');
3833     }
3834     $self->{map}->{$value} ||= $attr;
3835     $has_id = 1;
3836     },
3837 wakaba 1.70 ## TODO: HTML4 |name|
3838 wakaba 1.49 }, {
3839     %HTMLAttrStatus,
3840 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3841     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3842     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3843     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3844 wakaba 1.49 name => FEATURE_M12N10_REC_DEPRECATED,
3845 wakaba 1.50 onclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3846     ondblclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3847     onmousedown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3848     onmouseup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3849     onmouseover => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3850     onmousemove => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3851     onmouseout => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3852     onkeypress => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3853     onkeydown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3854     onkeyup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3855     title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3856 wakaba 1.66 })->(@_);
3857 wakaba 1.40 $self->{onerror}->(node => $item->{node}, type => 'attribute missing:id')
3858 wakaba 1.4 unless $has_id;
3859     },
3860 wakaba 1.59 check_start => sub {
3861     my ($self, $item, $element_state) = @_;
3862     $element_state->{in_map_original} = $self->{flag}->{in_map};
3863     $self->{flag}->{in_map} = 1;
3864 wakaba 1.79
3865     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3866     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3867 wakaba 1.59 },
3868     check_end => sub {
3869     my ($self, $item, $element_state) = @_;
3870     delete $self->{flag}->{in_map} unless $element_state->{in_map_original};
3871 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
3872 wakaba 1.59 },
3873 wakaba 1.1 };
3874    
3875     $Element->{$HTML_NS}->{area} = {
3876 wakaba 1.40 %HTMLEmptyChecker,
3877 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3878 wakaba 1.40 check_attrs => sub {
3879     my ($self, $item, $element_state) = @_;
3880 wakaba 1.1 my %attr;
3881     my $coords;
3882 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
3883 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
3884     $attr_ns = '' unless defined $attr_ns;
3885     my $attr_ln = $attr->manakai_local_name;
3886     my $checker;
3887 wakaba 1.73 my $status;
3888 wakaba 1.1 if ($attr_ns eq '') {
3889 wakaba 1.73 $status = {
3890     %HTMLAttrStatus,
3891     %HTMLM12NCommonAttrStatus,
3892     accesskey => FEATURE_M12N10_REC,
3893     alt => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3894     coords => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3895     href => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3896     hreflang => FEATURE_HTML5_DEFAULT,
3897     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3898     media => FEATURE_HTML5_DEFAULT,
3899     nohref => FEATURE_M12N10_REC,
3900     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3901     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3902     ping => FEATURE_HTML5_DEFAULT,
3903     rel => FEATURE_HTML5_DEFAULT,
3904     shape => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3905     tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3906     target => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3907     type => FEATURE_HTML5_DEFAULT,
3908     }->{$attr_ln};
3909    
3910 wakaba 1.1 $checker = {
3911 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
3912 wakaba 1.1 alt => sub { },
3913     ## NOTE: |alt| value has no conformance creteria.
3914     shape => $GetHTMLEnumeratedAttrChecker->({
3915     circ => -1, circle => 1,
3916     default => 1,
3917     poly => 1, polygon => -1,
3918     rect => 1, rectangle => -1,
3919     }),
3920     coords => sub {
3921     my ($self, $attr) = @_;
3922     my $value = $attr->value;
3923     if ($value =~ /\A-?[0-9]+(?>,-?[0-9]+)*\z/) {
3924     $coords = [split /,/, $value];
3925     } else {
3926     $self->{onerror}->(node => $attr,
3927     type => 'coords:syntax error');
3928     }
3929     },
3930 wakaba 1.70 nohref => $GetHTMLBooleanAttrChecker->('nohref'),
3931     target => $HTMLTargetAttrChecker,
3932 wakaba 1.1 href => $HTMLURIAttrChecker,
3933     ping => $HTMLSpaceURIsAttrChecker,
3934 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) },
3935 wakaba 1.1 media => $HTMLMQAttrChecker,
3936     hreflang => $HTMLLanguageTagAttrChecker,
3937     type => $HTMLIMTAttrChecker,
3938     }->{$attr_ln};
3939     if ($checker) {
3940     $attr{$attr_ln} = $attr;
3941 wakaba 1.73 } elsif ($attr_ln =~ /^data-/) {
3942     $checker = $HTMLDatasetAttrChecker;
3943     $status = $HTMLDatasetAttrStatus;
3944 wakaba 1.1 } else {
3945     $checker = $HTMLAttrChecker->{$attr_ln};
3946     }
3947     }
3948     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
3949 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
3950     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
3951     || $AttrStatus->{$attr_ns}->{''};
3952     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
3953 wakaba 1.62
3954 wakaba 1.1 if ($checker) {
3955 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
3956 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
3957 wakaba 1.54 #
3958 wakaba 1.1 } else {
3959     $self->{onerror}->(node => $attr, level => 'unsupported',
3960     type => 'attribute');
3961     ## ISSUE: No comformance createria for unknown attributes in the spec
3962     }
3963 wakaba 1.49
3964 wakaba 1.82 $self->_attr_status_info ($attr, $status);
3965 wakaba 1.1 }
3966    
3967     if (defined $attr{href}) {
3968 wakaba 1.4 $self->{has_hyperlink_element} = 1;
3969 wakaba 1.1 unless (defined $attr{alt}) {
3970 wakaba 1.40 $self->{onerror}->(node => $item->{node},
3971 wakaba 1.1 type => 'attribute missing:alt');
3972     }
3973     } else {
3974     for (qw/target ping rel media hreflang type alt/) {
3975     if (defined $attr{$_}) {
3976     $self->{onerror}->(node => $attr{$_},
3977     type => 'attribute not allowed');
3978     }
3979     }
3980     }
3981    
3982     my $shape = 'rectangle';
3983     if (defined $attr{shape}) {
3984     $shape = {
3985     circ => 'circle', circle => 'circle',
3986     default => 'default',
3987     poly => 'polygon', polygon => 'polygon',
3988     rect => 'rectangle', rectangle => 'rectangle',
3989     }->{lc $attr{shape}->value} || 'rectangle';
3990     ## TODO: ASCII lowercase?
3991     }
3992    
3993     if ($shape eq 'circle') {
3994     if (defined $attr{coords}) {
3995     if (defined $coords) {
3996     if (@$coords == 3) {
3997     if ($coords->[2] < 0) {
3998     $self->{onerror}->(node => $attr{coords},
3999     type => 'coords:out of range:2');
4000     }
4001     } else {
4002     $self->{onerror}->(node => $attr{coords},
4003     type => 'coords:number:3:'.@$coords);
4004     }
4005     } else {
4006     ## NOTE: A syntax error has been reported.
4007     }
4008     } else {
4009 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4010 wakaba 1.1 type => 'attribute missing:coords');
4011     }
4012     } elsif ($shape eq 'default') {
4013     if (defined $attr{coords}) {
4014     $self->{onerror}->(node => $attr{coords},
4015     type => 'attribute not allowed');
4016     }
4017     } elsif ($shape eq 'polygon') {
4018     if (defined $attr{coords}) {
4019     if (defined $coords) {
4020     if (@$coords >= 6) {
4021     unless (@$coords % 2 == 0) {
4022     $self->{onerror}->(node => $attr{coords},
4023     type => 'coords:number:even:'.@$coords);
4024     }
4025     } else {
4026     $self->{onerror}->(node => $attr{coords},
4027     type => 'coords:number:>=6:'.@$coords);
4028     }
4029     } else {
4030     ## NOTE: A syntax error has been reported.
4031     }
4032     } else {
4033 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4034 wakaba 1.1 type => 'attribute missing:coords');
4035     }
4036     } elsif ($shape eq 'rectangle') {
4037     if (defined $attr{coords}) {
4038     if (defined $coords) {
4039     if (@$coords == 4) {
4040     unless ($coords->[0] < $coords->[2]) {
4041     $self->{onerror}->(node => $attr{coords},
4042     type => 'coords:out of range:0');
4043     }
4044     unless ($coords->[1] < $coords->[3]) {
4045     $self->{onerror}->(node => $attr{coords},
4046     type => 'coords:out of range:1');
4047     }
4048     } else {
4049     $self->{onerror}->(node => $attr{coords},
4050     type => 'coords:number:4:'.@$coords);
4051     }
4052     } else {
4053     ## NOTE: A syntax error has been reported.
4054     }
4055     } else {
4056 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4057 wakaba 1.1 type => 'attribute missing:coords');
4058     }
4059     }
4060 wakaba 1.66
4061     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
4062 wakaba 1.1 },
4063 wakaba 1.59 check_start => sub {
4064     my ($self, $item, $element_state) = @_;
4065     unless ($self->{flag}->{in_map} or
4066     not $item->{node}->manakai_parent_element) {
4067     $self->{onerror}->(node => $item->{node},
4068     type => 'element not allowed:area',
4069     level => $self->{must_level});
4070     }
4071 wakaba 1.79
4072     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4073     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4074 wakaba 1.59 },
4075 wakaba 1.1 };
4076    
4077     $Element->{$HTML_NS}->{table} = {
4078 wakaba 1.40 %HTMLChecker,
4079 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4080 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4081 wakaba 1.86 cellpadding => $HTMLLengthAttrChecker,
4082     cellspacing => $HTMLLengthAttrChecker,
4083 wakaba 1.69 frame => $GetHTMLEnumeratedAttrChecker->({
4084     void => 1, above => 1, below => 1, hsides => 1, vsides => 1,
4085     lhs => 1, rhs => 1, box => 1, border => 1,
4086     }),
4087     rules => $GetHTMLEnumeratedAttrChecker->({
4088     none => 1, groups => 1, rows => 1, cols => 1, all => 1,
4089     }),
4090     summary => sub {}, ## NOTE: %Text; in HTML4.
4091     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## %Pixels;
4092     }, {
4093 wakaba 1.49 %HTMLAttrStatus,
4094 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4095 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
4096     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
4097     border => FEATURE_M12N10_REC,
4098     cellpadding => FEATURE_M12N10_REC,
4099     cellspacing => FEATURE_M12N10_REC,
4100 wakaba 1.61 cols => FEATURE_RFC1942,
4101 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
4102     dataformatas => FEATURE_HTML4_REC_RESERVED,
4103     datapagesize => FEATURE_M12N10_REC,
4104     datasrc => FEATURE_HTML4_REC_RESERVED,
4105     frame => FEATURE_M12N10_REC,
4106 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4107 wakaba 1.49 rules => FEATURE_M12N10_REC,
4108     summary => FEATURE_M12N10_REC,
4109     width => FEATURE_M12N10_REC,
4110     }),
4111 wakaba 1.40 check_start => sub {
4112     my ($self, $item, $element_state) = @_;
4113     $element_state->{phase} = 'before caption';
4114 wakaba 1.66
4115     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4116 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4117     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4118 wakaba 1.40 },
4119     check_child_element => sub {
4120     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4121     $child_is_transparent, $element_state) = @_;
4122     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4123     $self->{onerror}->(node => $child_el,
4124     type => 'element not allowed:minus',
4125     level => $self->{must_level});
4126     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4127     #
4128     } elsif ($element_state->{phase} eq 'in tbodys') {
4129     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4130     #$element_state->{phase} = 'in tbodys';
4131     } elsif (not $element_state->{has_tfoot} and
4132     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4133     $element_state->{phase} = 'after tfoot';
4134     $element_state->{has_tfoot} = 1;
4135     } else {
4136     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4137     }
4138     } elsif ($element_state->{phase} eq 'in trs') {
4139     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4140     #$element_state->{phase} = 'in trs';
4141     } elsif (not $element_state->{has_tfoot} and
4142     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4143     $element_state->{phase} = 'after tfoot';
4144     $element_state->{has_tfoot} = 1;
4145     } else {
4146     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4147     }
4148     } elsif ($element_state->{phase} eq 'after thead') {
4149     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4150     $element_state->{phase} = 'in tbodys';
4151     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4152     $element_state->{phase} = 'in trs';
4153     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4154     $element_state->{phase} = 'in tbodys';
4155     $element_state->{has_tfoot} = 1;
4156     } else {
4157     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4158     }
4159     } elsif ($element_state->{phase} eq 'in colgroup') {
4160     if ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
4161     $element_state->{phase} = 'in colgroup';
4162     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
4163     $element_state->{phase} = 'after thead';
4164     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4165     $element_state->{phase} = 'in tbodys';
4166     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4167     $element_state->{phase} = 'in trs';
4168     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4169     $element_state->{phase} = 'in tbodys';
4170     $element_state->{has_tfoot} = 1;
4171     } else {
4172     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4173     }
4174     } elsif ($element_state->{phase} eq 'before caption') {
4175     if ($child_nsuri eq $HTML_NS and $child_ln eq 'caption') {
4176     $element_state->{phase} = 'in colgroup';
4177     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
4178     $element_state->{phase} = 'in colgroup';
4179     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
4180     $element_state->{phase} = 'after thead';
4181     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4182     $element_state->{phase} = 'in tbodys';
4183     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4184     $element_state->{phase} = 'in trs';
4185     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4186     $element_state->{phase} = 'in tbodys';
4187     $element_state->{has_tfoot} = 1;
4188     } else {
4189     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4190     }
4191     } elsif ($element_state->{phase} eq 'after tfoot') {
4192     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4193     } else {
4194     die "check_child_element: Bad |table| phase: $element_state->{phase}";
4195     }
4196     },
4197     check_child_text => sub {
4198     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4199     if ($has_significant) {
4200     $self->{onerror}->(node => $child_node, type => 'character not allowed');
4201 wakaba 1.1 }
4202 wakaba 1.40 },
4203     check_end => sub {
4204     my ($self, $item, $element_state) = @_;
4205 wakaba 1.1
4206     ## Table model errors
4207     require Whatpm::HTMLTable;
4208 wakaba 1.87 my $table = Whatpm::HTMLTable->form_table ($item->{node}, sub {
4209 wakaba 1.1 my %opt = @_;
4210 wakaba 1.84 $opt{type} = 'table:' . $opt{type};
4211     $self->{onerror}->(%opt);
4212 wakaba 1.87 }, $self->{must_level});
4213     Whatpm::HTMLTable->assign_header
4214     ($table, $self->{onerror}, $self->{must_level});
4215     push @{$self->{return}->{table}}, $table;
4216 wakaba 1.1
4217 wakaba 1.40 $HTMLChecker{check_end}->(@_);
4218 wakaba 1.1 },
4219     };
4220    
4221     $Element->{$HTML_NS}->{caption} = {
4222 wakaba 1.40 %HTMLPhrasingContentChecker,
4223 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4224 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4225     align => $GetHTMLEnumeratedAttrChecker->({
4226     top => 1, bottom => 1, left => 1, right => 1,
4227     }),
4228     }, {
4229 wakaba 1.49 %HTMLAttrStatus,
4230 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4231 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
4232 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4233 wakaba 1.49 }),
4234 wakaba 1.1 };
4235    
4236 wakaba 1.69 my %cellalign = (
4237     ## HTML4 %cellhalign;
4238 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
4239     left => 1, center => 1, right => 1, justify => 1, char => 1,
4240     }),
4241     char => sub {
4242     my ($self, $attr) = @_;
4243 wakaba 1.69
4244 wakaba 1.70 ## NOTE: "character" or |%Character;| in HTML4.
4245    
4246     my $value = $attr->value;
4247     if (length $value != 1) {
4248     $self->{onerror}->(node => $attr, type => 'char:syntax error',
4249     level => $self->{fact_level}); ## TODO: type
4250     }
4251     },
4252 wakaba 1.86 charoff => $HTMLLengthAttrChecker,
4253    
4254 wakaba 1.69 ## HTML4 %cellvalign;
4255 wakaba 1.70 valign => $GetHTMLEnumeratedAttrChecker->({
4256     top => 1, middle => 1, bottom => 1, baseline => 1,
4257     }),
4258 wakaba 1.69 );
4259    
4260 wakaba 1.1 $Element->{$HTML_NS}->{colgroup} = {
4261 wakaba 1.40 %HTMLEmptyChecker,
4262 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4263 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4264 wakaba 1.69 %cellalign,
4265 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4266     ## NOTE: Defined only if "the |colgroup| element contains no |col| elements"
4267     ## TODO: "attribute not supported" if |col|.
4268     ## ISSUE: MUST NOT if any |col|?
4269     ## ISSUE: MUST NOT for |<colgroup span="1"><any><col/></any></colgroup>| (though non-conforming)?
4270 wakaba 1.49 }, {
4271     %HTMLAttrStatus,
4272 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4273 wakaba 1.49 align => FEATURE_M12N10_REC,
4274     char => FEATURE_M12N10_REC,
4275     charoff => FEATURE_M12N10_REC,
4276 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4277 wakaba 1.82 span => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4278 wakaba 1.49 valign => FEATURE_M12N10_REC,
4279     width => FEATURE_M12N10_REC,
4280 wakaba 1.1 }),
4281 wakaba 1.40 check_child_element => sub {
4282     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4283     $child_is_transparent, $element_state) = @_;
4284     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4285     $self->{onerror}->(node => $child_el,
4286     type => 'element not allowed:minus',
4287     level => $self->{must_level});
4288     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4289     #
4290     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'col') {
4291     #
4292     } else {
4293     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4294     }
4295     },
4296     check_child_text => sub {
4297     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4298     if ($has_significant) {
4299     $self->{onerror}->(node => $child_node, type => 'character not allowed');
4300 wakaba 1.1 }
4301     },
4302     };
4303    
4304     $Element->{$HTML_NS}->{col} = {
4305 wakaba 1.40 %HTMLEmptyChecker,
4306 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4307 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4308 wakaba 1.69 %cellalign,
4309 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4310 wakaba 1.49 }, {
4311     %HTMLAttrStatus,
4312 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4313 wakaba 1.49 align => FEATURE_M12N10_REC,
4314     char => FEATURE_M12N10_REC,
4315     charoff => FEATURE_M12N10_REC,
4316 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4317 wakaba 1.82 span => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4318 wakaba 1.49 valign => FEATURE_M12N10_REC,
4319     width => FEATURE_M12N10_REC,
4320 wakaba 1.1 }),
4321     };
4322    
4323     $Element->{$HTML_NS}->{tbody} = {
4324 wakaba 1.40 %HTMLChecker,
4325 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4326 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4327     %cellalign,
4328     }, {
4329 wakaba 1.49 %HTMLAttrStatus,
4330 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4331 wakaba 1.49 align => FEATURE_M12N10_REC,
4332     char => FEATURE_M12N10_REC,
4333     charoff => FEATURE_M12N10_REC,
4334 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4335 wakaba 1.49 valign => FEATURE_M12N10_REC,
4336     }),
4337 wakaba 1.40 check_child_element => sub {
4338     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4339     $child_is_transparent, $element_state) = @_;
4340     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4341     $self->{onerror}->(node => $child_el,
4342     type => 'element not allowed:minus',
4343     level => $self->{must_level});
4344     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4345     #
4346     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4347 wakaba 1.84 #
4348 wakaba 1.40 } else {
4349     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4350     }
4351     },
4352     check_child_text => sub {
4353     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4354     if ($has_significant) {
4355     $self->{onerror}->(node => $child_node, type => 'character not allowed');
4356 wakaba 1.1 }
4357 wakaba 1.40 },
4358 wakaba 1.1 };
4359    
4360     $Element->{$HTML_NS}->{thead} = {
4361 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
4362 wakaba 1.1 };
4363    
4364     $Element->{$HTML_NS}->{tfoot} = {
4365 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
4366 wakaba 1.1 };
4367    
4368     $Element->{$HTML_NS}->{tr} = {
4369 wakaba 1.40 %HTMLChecker,
4370 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4371 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4372     %cellalign,
4373     bgcolor => $HTMLColorAttrChecker,
4374     }, {
4375 wakaba 1.49 %HTMLAttrStatus,
4376 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4377 wakaba 1.49 align => FEATURE_M12N10_REC,
4378     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
4379     char => FEATURE_M12N10_REC,
4380     charoff => FEATURE_M12N10_REC,
4381 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4382 wakaba 1.49 valign => FEATURE_M12N10_REC,
4383     }),
4384 wakaba 1.40 check_child_element => sub {
4385     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4386     $child_is_transparent, $element_state) = @_;
4387     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4388     $self->{onerror}->(node => $child_el,
4389     type => 'element not allowed:minus',
4390     level => $self->{must_level});
4391     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4392     #
4393     } elsif ($child_nsuri eq $HTML_NS and
4394     ($child_ln eq 'td' or $child_ln eq 'th')) {
4395 wakaba 1.84 #
4396 wakaba 1.40 } else {
4397     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4398     }
4399     },
4400     check_child_text => sub {
4401     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4402     if ($has_significant) {
4403     $self->{onerror}->(node => $child_node, type => 'character not allowed');
4404 wakaba 1.1 }
4405     },
4406     };
4407    
4408     $Element->{$HTML_NS}->{td} = {
4409 wakaba 1.72 %HTMLFlowContentChecker,
4410 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4411 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4412 wakaba 1.69 %cellalign,
4413     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
4414     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
4415     bgcolor => $HTMLColorAttrChecker,
4416 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4417 wakaba 1.87 headers => sub {
4418     ## NOTE: Will be checked by Whatpm::HTMLTable->assign_header.
4419     ## Though that method does not check the |headers| attribute of a
4420     ## |td| element if the element does not form a table, in that case
4421     ## the |td| element is non-conforming anyway.
4422     },
4423 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
4424 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4425 wakaba 1.69 scope => $GetHTMLEnumeratedAttrChecker
4426     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
4427 wakaba 1.49 }, {
4428     %HTMLAttrStatus,
4429 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4430     abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4431 wakaba 1.49 align => FEATURE_M12N10_REC,
4432 wakaba 1.82 axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4433 wakaba 1.49 bgcolor => FEATURE_M12N10_REC_DEPRECATED,
4434     char => FEATURE_M12N10_REC,
4435     charoff => FEATURE_M12N10_REC,
4436 wakaba 1.82 colspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4437 wakaba 1.87 headers => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4438 wakaba 1.49 height => FEATURE_M12N10_REC_DEPRECATED,
4439 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4440 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
4441 wakaba 1.82 rowspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4442     scope => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4443 wakaba 1.49 valign => FEATURE_M12N10_REC,
4444     width => FEATURE_M12N10_REC_DEPRECATED,
4445 wakaba 1.1 }),
4446     };
4447    
4448     $Element->{$HTML_NS}->{th} = {
4449 wakaba 1.40 %HTMLPhrasingContentChecker,
4450 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4451 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4452 wakaba 1.69 %cellalign,
4453     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
4454     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
4455     bgcolor => $HTMLColorAttrChecker,
4456 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4457 wakaba 1.87 ## TODO: HTML4(?) |headers|
4458 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
4459 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4460     scope => $GetHTMLEnumeratedAttrChecker
4461     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
4462 wakaba 1.49 }, {
4463     %HTMLAttrStatus,
4464 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4465     abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4466 wakaba 1.49 align => FEATURE_M12N10_REC,
4467 wakaba 1.82 axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4468 wakaba 1.49 bgcolor => FEATURE_M12N10_REC_DEPRECATED,
4469     char => FEATURE_M12N10_REC,
4470     charoff => FEATURE_M12N10_REC,
4471 wakaba 1.82 colspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4472     headers => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4473 wakaba 1.49 height => FEATURE_M12N10_REC_DEPRECATED,
4474 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4475 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
4476 wakaba 1.82 rowspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4477     scope => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4478 wakaba 1.49 valign => FEATURE_M12N10_REC,
4479     width => FEATURE_M12N10_REC_DEPRECATED,
4480 wakaba 1.1 }),
4481     };
4482    
4483 wakaba 1.52 $Element->{$HTML_NS}->{form} = {
4484 wakaba 1.72 %HTMLFlowContentChecker, ## NOTE: Flow* [WF2]
4485 wakaba 1.56 ## TODO: form in form is allowed in XML [WF2]
4486 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
4487     check_attrs => $GetHTMLAttrsChecker->({
4488 wakaba 1.56 accept => $AttrCheckerNotImplemented, ## TODO: ContentTypes [WF2]
4489 wakaba 1.52 'accept-charset' => $AttrCheckerNotImplemented, ## TODO: Charsets
4490     action => $HTMLURIAttrChecker, ## TODO: "User agent behavior for a value other than HTTP URI is undefined" [HTML4]
4491 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
4492     enctype => $HTMLIMTAttrChecker, ## TODO: "multipart/form-data" should be used when type=file is used [HTML4] ## TODO: MUST NOT parameter [WF2]
4493     method => $GetHTMLEnumeratedAttrChecker->({
4494     get => 1, post => 1, put => 1, delete => 1,
4495     }),
4496 wakaba 1.52 ## NOTE: "get" SHOULD be used for idempotent submittion,
4497     ## "post" SHOULD be used otherwise [HTML4]. This cannot be tested.
4498     name => sub { }, # CDATA in HTML4 ## TODO: must be same as |id| (informative!) [XHTML10]
4499 wakaba 1.56 onreceived => $HTMLEventHandlerAttrChecker,
4500     replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
4501 wakaba 1.52 target => $HTMLTargetAttrChecker,
4502     ## TODO: Warn for combination whose behavior is not defined.
4503     }, {
4504     %HTMLAttrStatus,
4505     %HTMLM12NCommonAttrStatus,
4506 wakaba 1.56 accept => FEATURE_WF2 | FEATURE_M12N10_REC,
4507 wakaba 1.52 'accept-charset' => FEATURE_M12N10_REC,
4508 wakaba 1.56 action => FEATURE_WF2 | FEATURE_M12N10_REC,
4509     data => FEATURE_WF2,
4510     enctype => FEATURE_WF2 | FEATURE_M12N10_REC,
4511 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4512 wakaba 1.56 method => FEATURE_WF2 | FEATURE_M12N10_REC,
4513 wakaba 1.52 name => FEATURE_M12N10_REC_DEPRECATED,
4514 wakaba 1.56 onreceived => FEATURE_WF2,
4515 wakaba 1.52 onreset => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4516     onsubmit => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4517 wakaba 1.56 replace => FEATURE_WF2,
4518 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
4519     sdasuff => FEATURE_HTML20_RFC,
4520 wakaba 1.52 target => FEATURE_M12N10_REC,
4521     }),
4522     ## TODO: Tests
4523     ## TODO: Tests for <nest/> in <form>
4524 wakaba 1.66 check_start => sub {
4525     my ($self, $item, $element_state) = @_;
4526    
4527     $element_state->{uri_info}->{action}->{type}->{action} = 1;
4528     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
4529 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4530     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4531 wakaba 1.66 },
4532 wakaba 1.52 };
4533    
4534     $Element->{$HTML_NS}->{fieldset} = {
4535 wakaba 1.72 %HTMLFlowContentChecker, ## NOTE: legend, %Flow; ## TODO: legend
4536 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
4537 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
4538     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4539     ## TODO: form [WF2]
4540     }, {
4541 wakaba 1.52 %HTMLAttrStatus,
4542     %HTMLM12NCommonAttrStatus,
4543 wakaba 1.56 disabled => FEATURE_WF2,
4544     form => FEATURE_WF2,
4545 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4546     }),
4547     ## TODO: Tests
4548     ## TODO: Tests for <nest/> in <fieldset>
4549     };
4550    
4551     $Element->{$HTML_NS}->{input} = {
4552 wakaba 1.56 %HTMLEmptyChecker, ## MUST [WF2]
4553 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
4554     check_attrs => $GetHTMLAttrsChecker->({
4555 wakaba 1.56 accept => $AttrCheckerNotImplemented, ## TODO: ContentTypes [WF2]
4556 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4557 wakaba 1.56 action => $HTMLURIAttrChecker,
4558 wakaba 1.52 align => $GetHTMLEnumeratedAttrChecker->({
4559     top => 1, middle => 1, bottom => 1, left => 1, right => 1,
4560     }),
4561     alt => sub {}, ## NOTE: Text [M12N] ## TODO: |alt| should be provided for |type=image| [HTML4]
4562     ## NOTE: HTML4 has a "should" for accessibility, which cannot be tested
4563     ## here.
4564 wakaba 1.56 autocomplete => $GetHTMLEnumeratedAttrChecker->({on => 1, off => 1}),
4565     autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
4566 wakaba 1.52 checked => $GetHTMLBooleanAttrChecker->('checked'),
4567     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4568 wakaba 1.56 enctype => $HTMLIMTAttrChecker,
4569     ## TODO: form [WF2]
4570     ## TODO: inputmode [WF2]
4571 wakaba 1.52 ismap => $GetHTMLBooleanAttrChecker->('ismap'),
4572 wakaba 1.56 ## TODO: list [WF2]
4573     ## TODO: max [WF2]
4574 wakaba 1.52 maxlength => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4575 wakaba 1.56 method => $GetHTMLEnumeratedAttrChecker->({
4576     get => 1, post => 1, put => 1, delete => 1,
4577     }),
4578     ## TODO: min [WF2]
4579 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
4580     readonly => $GetHTMLBooleanAttrChecker->('readonly'),
4581 wakaba 1.56 replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
4582     required => $GetHTMLBooleanAttrChecker->('required'),
4583 wakaba 1.52 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4584     src => $HTMLURIAttrChecker,
4585 wakaba 1.56 ## TODO: step [WF2]
4586     target => $HTMLTargetAttrChecker,
4587 wakaba 1.80 ## NOTE: According to Web Forms 2.0, |input| attribute has |template|
4588     ## attribute to support the |add| button type (as part of repetition
4589     ## template feature). It conflicts with the |template| global attribute
4590     ## introduced as part of the data template feature.
4591     ## NOTE: |template| attribute as defined in Web Forms 2.0 has no
4592     ## author requirement.
4593 wakaba 1.52 type => $GetHTMLEnumeratedAttrChecker->({
4594     text => 1, password => 1, checkbox => 1, radio => 1, submit => 1,
4595     reset => 1, file => 1, hidden => 1, image => 1, button => 1,
4596 wakaba 1.56 ## [WF2]
4597     datatime => 1, 'datetime-local' => 1, date => 1, month => 1, week => 1,
4598     time => 1, number => 1, range => 1, email => 1, url => 1,
4599     add => 1, remove => 1, 'move-up' => 1, 'move-down' => 1,
4600 wakaba 1.52 }),
4601     usemap => $HTMLUsemapAttrChecker,
4602 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]
4603     ## TODO: "authors should ensure that in each set of radio buttons that one is initially "on"." [HTML4] [WF2]
4604 wakaba 1.52 }, {
4605     %HTMLAttrStatus,
4606     %HTMLM12NCommonAttrStatus,
4607 wakaba 1.56 accept => FEATURE_WF2 | FEATURE_M12N10_REC,
4608 wakaba 1.61 'accept-charset' => FEATURE_HTML2X_RFC,
4609 wakaba 1.52 accesskey => FEATURE_M12N10_REC,
4610 wakaba 1.56 action => FEATURE_WF2,
4611 wakaba 1.52 align => FEATURE_M12N10_REC_DEPRECATED,
4612     alt => FEATURE_M12N10_REC,
4613 wakaba 1.56 autocomplete => FEATURE_WF2,
4614     autofocus => FEATURE_WF2,
4615 wakaba 1.52 checked => FEATURE_M12N10_REC,
4616     datafld => FEATURE_HTML4_REC_RESERVED,
4617     dataformatas => FEATURE_HTML4_REC_RESERVED,
4618     datasrc => FEATURE_HTML4_REC_RESERVED,
4619 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
4620 wakaba 1.65 enctype => FEATURE_WF2,
4621 wakaba 1.56 form => FEATURE_WF2,
4622     inputmode => FEATURE_WF2 | FEATURE_XHTMLBASIC11_CR,
4623 wakaba 1.52 ismap => FEATURE_M12N10_REC,
4624     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4625 wakaba 1.56 list => FEATURE_WF2,
4626     max => FEATURE_WF2,
4627     maxlength => FEATURE_WF2 | FEATURE_M12N10_REC,
4628     method => FEATURE_WF2,
4629     min => FEATURE_WF2,
4630 wakaba 1.52 name => FEATURE_M12N10_REC,
4631     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4632     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4633     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4634     onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4635 wakaba 1.56 readonly => FEATURE_WF2 | FEATURE_M12N10_REC,
4636 wakaba 1.65 replace => FEATURE_WF2,
4637 wakaba 1.56 required => FEATURE_WF2,
4638 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
4639 wakaba 1.56 size => FEATURE_WF2_DEPRECATED | FEATURE_M12N10_REC,
4640 wakaba 1.52 src => FEATURE_M12N10_REC,
4641 wakaba 1.56 step => FEATURE_WF2,
4642 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4643 wakaba 1.65 target => FEATURE_WF2,
4644 wakaba 1.56 template => FEATURE_WF2,
4645 wakaba 1.52 type => FEATURE_M12N10_REC,
4646     usemap => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC,
4647     value => FEATURE_M12N10_REC,
4648     }),
4649     ## TODO: Tests
4650     ## TODO: Tests for <nest/> in <input>
4651 wakaba 1.66 check_start => sub {
4652     my ($self, $item, $element_state) = @_;
4653    
4654     $element_state->{uri_info}->{action}->{type}->{action} = 1;
4655     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4656     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4657 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4658     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4659 wakaba 1.66 },
4660 wakaba 1.52 };
4661    
4662 wakaba 1.56 ## TODO: Form |name| attributes: MUST NOT conflict with RFC 3106 [WF2]
4663    
4664 wakaba 1.80 ## NOTE: "authors who are nesting repetition blocks should position such
4665     ## [repetition-block-related] buttons carefully to make clear which block a
4666     ## button applies to.": I have no idea how this can be tested.
4667    
4668 wakaba 1.52 $Element->{$HTML_NS}->{button} = {
4669 wakaba 1.72 %HTMLFlowContentChecker, ## NOTE: %Flow; - something [XHTML10]
4670 wakaba 1.52 ## TODO: -A|%formctrl;|form|fieldset [HTML4]
4671     ## TODO: image map (img) in |button| is "illegal" [HTML4].
4672     status => FEATURE_WF2 | FEATURE_M12N10_REC,
4673     check_attrs => $GetHTMLAttrsChecker->({
4674 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4675 wakaba 1.56 action => $HTMLURIAttrChecker,
4676     autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
4677 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4678 wakaba 1.56 ## TODO: form [WF2]
4679     method => $GetHTMLEnumeratedAttrChecker->({
4680     get => 1, post => 1, put => 1, delete => 1,
4681     }),
4682 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
4683 wakaba 1.56 oninvalid => $HTMLEventHandlerAttrChecker,
4684     replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
4685     target => $HTMLTargetAttrChecker,
4686 wakaba 1.80 ## NOTE: According to Web Forms 2.0, |button| attribute has |template|
4687     ## attribute to support the |add| button type (as part of repetition
4688     ## template feature). It conflicts with the |template| global attribute
4689     ## introduced as part of the data template feature.
4690     ## NOTE: |template| attribute as defined in Web Forms 2.0 has no
4691     ## author requirement.
4692 wakaba 1.52 type => $GetHTMLEnumeratedAttrChecker->({
4693     button => 1, submit => 1, reset => 1,
4694 wakaba 1.80 ## [WF2]
4695     add => 1, remove => 1, 'move-up' => 1, 'move-down' => 1,
4696 wakaba 1.52 }),
4697     value => sub {}, ## NOTE: CDATA [M12N]
4698     }, {
4699     %HTMLAttrStatus,
4700     %HTMLM12NCommonAttrStatus,
4701     accesskey => FEATURE_M12N10_REC,
4702 wakaba 1.56 action => FEATURE_WF2,
4703     autofocus => FEATURE_WF2,
4704 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
4705     dataformatas => FEATURE_HTML4_REC_RESERVED,
4706     datasrc => FEATURE_HTML4_REC_RESERVED,
4707 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
4708     enctype => FEATURE_WF2,
4709     form => FEATURE_WF2,
4710 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4711 wakaba 1.56 method => FEATURE_WF2,
4712 wakaba 1.52 name => FEATURE_M12N10_REC,
4713     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4714     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4715 wakaba 1.56 oninvalid => FEATURE_WF2,
4716     replace => FEATURE_WF2,
4717 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4718 wakaba 1.56 target => FEATURE_WF2,
4719     template => FEATURE_WF2,
4720 wakaba 1.52 type => FEATURE_M12N10_REC,
4721     value => FEATURE_M12N10_REC,
4722     }),
4723     ## TODO: Tests
4724     ## TODO: Tests for <nest/> in <button>
4725 wakaba 1.66 check_start => sub {
4726     my ($self, $item, $element_state) = @_;
4727    
4728     $element_state->{uri_info}->{action}->{type}->{action} = 1;
4729     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4730 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4731     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4732 wakaba 1.66 },
4733 wakaba 1.52 };
4734    
4735     $Element->{$HTML_NS}->{label} = {
4736     %HTMLPhrasingContentChecker, ## NOTE: %Inline - label [XHTML10] ## TODO: -label
4737 wakaba 1.56 ## TODO: At most one form control [WF2]
4738 wakaba 1.82 status => FEATURE_WF2 | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4739 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
4740 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4741 wakaba 1.52 for => $AttrCheckerNotImplemented, ## TODO: IDREF ## TODO: Must be |id| of control [HTML4] ## TODO: Or, "may only contain one control element"
4742     }, {
4743     %HTMLAttrStatus,
4744 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4745 wakaba 1.56 accesskey => FEATURE_WF2 | FEATURE_M12N10_REC,
4746 wakaba 1.52 for => FEATURE_M12N10_REC,
4747     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4748     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4749     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4750     }),
4751     ## TODO: Tests
4752     ## TODO: Tests for <nest/> in <label>
4753     };
4754    
4755     $Element->{$HTML_NS}->{select} = {
4756 wakaba 1.72 %HTMLFlowContentChecker, ## TODO: (optgroup|option)* [HTML4] + [WF2] ## TODO: SHOULD avoid empty and visible [WF2]
4757 wakaba 1.52 ## TODO: author should SELECTED at least one OPTION in non-MULTIPLE case [HTML4].
4758     ## TODO: more than one OPTION with SELECTED in non-MULTIPLE case is "error" [HTML4]
4759     status => FEATURE_WF2 | FEATURE_M12N10_REC,
4760 wakaba 1.56 is_root => 1, ## TODO: SHOULD NOT in application/xhtml+xml [WF2]
4761 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
4762 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4763 wakaba 1.56 autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
4764 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4765 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
4766     ## TODO: form [WF2]
4767 wakaba 1.52 multiple => $GetHTMLBooleanAttrChecker->('multiple'),
4768     name => sub {}, ## NOTE: CDATA [M12N]
4769 wakaba 1.56 oninvalid => $HTMLEventHandlerAttrChecker,
4770     ## TODO: pattern [WF2] ## TODO: |title| semantics
4771 wakaba 1.52 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4772     }, {
4773     %HTMLAttrStatus,
4774     %HTMLM12NCommonAttrStatus,
4775 wakaba 1.56 accesskey => FEATURE_WF2,
4776     autofocus => FEATURE_WF2,
4777     data => FEATURE_WF2,
4778 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
4779     dataformatas => FEATURE_HTML4_REC_RESERVED,
4780     datasrc => FEATURE_HTML4_REC_RESERVED,
4781 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
4782     form => FEATURE_WF2,
4783 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4784     multiple => FEATURE_M12N10_REC,
4785     name => FEATURE_M12N10_REC,
4786     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4787     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4788     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4789 wakaba 1.56 oninvalid => FEATURE_WF2,
4790     pattern => FEATURE_WF2,
4791 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
4792     sdapref => FEATURE_HTML20_RFC,
4793 wakaba 1.52 size => FEATURE_M12N10_REC,
4794     tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4795     }),
4796     ## TODO: Tests
4797     ## TODO: Tests for <nest/> in <select>
4798 wakaba 1.66 check_start => sub {
4799     my ($self, $item, $element_state) = @_;
4800    
4801     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
4802     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4803 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4804     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4805 wakaba 1.66 },
4806 wakaba 1.52 };
4807 wakaba 1.1
4808 wakaba 1.52 $Element->{$HTML_NS}->{datalist} = {
4809 wakaba 1.72 %HTMLFlowContentChecker, ## TODO: (transparent | option)*
4810 wakaba 1.56 ## TODO: |option| child MUST be empty [WF2]
4811 wakaba 1.52 status => FEATURE_WF2,
4812 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
4813     data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
4814     }, {
4815 wakaba 1.52 %HTMLAttrStatus,
4816 wakaba 1.56 data => FEATURE_WF2,
4817 wakaba 1.52 }),
4818     ## TODO: Tests
4819     ## TODO: Tests for <nest/> in <datalist>
4820 wakaba 1.66 check_start => sub {
4821     my ($self, $item, $element_state) = @_;
4822    
4823     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
4824 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4825     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4826 wakaba 1.66 },
4827 wakaba 1.52 };
4828 wakaba 1.49
4829 wakaba 1.52 $Element->{$HTML_NS}->{optgroup} = {
4830 wakaba 1.72 %HTMLFlowContentChecker, ## TODO: (option|optgroup)* [HTML4] + [WF2] SHOULD avoid empty and visible [WF2]
4831 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
4832     check_attrs => $GetHTMLAttrsChecker->({
4833     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4834     label => sub {}, ## NOTE: Text [M12N] ## TODO: required
4835     }, {
4836     %HTMLAttrStatus,
4837     %HTMLM12NCommonAttrStatus,
4838 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
4839 wakaba 1.52 label => FEATURE_M12N10_REC,
4840     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4841     }),
4842     ## TODO: Tests
4843     ## TODO: Tests for <nest/> in <optgroup>
4844     };
4845    
4846     $Element->{$HTML_NS}->{option} = {
4847     %HTMLTextChecker,
4848     status => FEATURE_WF2 | FEATURE_M12N10_REC,
4849     check_attrs => $GetHTMLAttrsChecker->({
4850     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4851     label => sub {}, ## NOTE: Text [M12N]
4852     selected => $GetHTMLBooleanAttrChecker->('selected'),
4853     value => sub {}, ## NOTE: CDATA [M12N]
4854     }, {
4855     %HTMLAttrStatus,
4856     %HTMLM12NCommonAttrStatus,
4857 wakaba 1.56 disabled => FEATURE_WF2, FEATURE_M12N10_REC,
4858 wakaba 1.52 label => FEATURE_M12N10_REC,
4859     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4860 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
4861     sdapref => FEATURE_HTML20_RFC,
4862 wakaba 1.52 selected => FEATURE_M12N10_REC,
4863     value => FEATURE_M12N10_REC,
4864     }),
4865     ## TODO: Tests
4866     ## TODO: Tests for <nest/> in <option>
4867     };
4868 wakaba 1.49
4869 wakaba 1.52 $Element->{$HTML_NS}->{textarea} = {
4870     %HTMLTextChecker,
4871     status => FEATURE_WF2 | FEATURE_M12N10_REC,
4872     check_attrs => $GetHTMLAttrsChecker->({
4873 wakaba 1.56 accept => $HTMLIMTAttrChecker, ## TODO: MUST be a text-based type
4874 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4875 wakaba 1.56 autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
4876     cols => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## TODO: SHOULD if wrap=hard [WF2]
4877 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4878 wakaba 1.56 ## TODO: form [WF2]
4879     ## TODO: inputmode [WF2]
4880     maxlength => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4881 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
4882 wakaba 1.56 ## TODO: pattern [WF2] ## TODO: |title| special semantics
4883 wakaba 1.52 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
4884 wakaba 1.56 required => $GetHTMLBooleanAttrChecker->('required'),
4885     rows => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4886     oninvalid => $HTMLEventHandlerAttrChecker,
4887     wrap => $GetHTMLEnumeratedAttrChecker->({soft => 1, hard => 1}),
4888 wakaba 1.52 }, {
4889     %HTMLAttrStatus,
4890     %HTMLM12NCommonAttrStatus,
4891 wakaba 1.56 accept => FEATURE_WF2,
4892 wakaba 1.61 'accept-charset' => FEATURE_HTML2X_RFC,
4893 wakaba 1.52 accesskey => FEATURE_M12N10_REC,
4894 wakaba 1.56 autofocus => FEATURE_WF2,
4895 wakaba 1.52 cols => FEATURE_M12N10_REC,
4896     datafld => FEATURE_HTML4_REC_RESERVED,
4897 wakaba 1.49 dataformatas => FEATURE_HTML4_REC_RESERVED,
4898     datasrc => FEATURE_HTML4_REC_RESERVED,
4899 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
4900     form => FEATURE_WF2,
4901     inputmode => FEATURE_WF2 | FEATURE_XHTMLBASIC11_CR,
4902 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4903 wakaba 1.56 maxlength => FEATURE_WF2,
4904 wakaba 1.52 name => FEATURE_M12N10_REC,
4905     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4906     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4907     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4908 wakaba 1.56 oninvalid => FEATURE_WF2,
4909 wakaba 1.52 onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4910 wakaba 1.56 pattern => FEATURE_WF2,
4911     readonly => FEATURE_WF2 | FEATURE_M12N10_REC,
4912     required => FEATURE_WF2,
4913 wakaba 1.61 rows => FEATURE_M12N10_REC,
4914     sdaform => FEATURE_HTML20_RFC,
4915     sdapref => FEATURE_HTML20_RFC,
4916 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4917 wakaba 1.56 wrap => FEATURE_WF2,
4918 wakaba 1.52 }),
4919     ## TODO: Tests
4920     ## TODO: Tests for <nest/> in <textarea>
4921 wakaba 1.66 check_start => sub {
4922     my ($self, $item, $element_state) = @_;
4923    
4924     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
4925 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4926     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4927 wakaba 1.66 },
4928 wakaba 1.52 };
4929 wakaba 1.49
4930 wakaba 1.52 $Element->{$HTML_NS}->{output} = {
4931 wakaba 1.56 %HTMLPhrasingContentChecker, ## Inline [WF2]
4932 wakaba 1.52 status => FEATURE_WF2,
4933 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
4934     ## TODO: for [WF2]
4935     ## TODO: form [WF2]
4936     ## TODO: name [WF2]
4937     ## onformchange[WF2]
4938     ## onforminput[WF2]
4939     }, {
4940 wakaba 1.52 %HTMLAttrStatus,
4941 wakaba 1.56 for => FEATURE_WF2,
4942     form => FEATURE_WF2,
4943     name => FEATURE_WF2,
4944     onchange => FEATURE_HTML5_DEFAULT | FEATURE_WF2,
4945     onformchange => FEATURE_WF2,
4946     onforminput => FEATURE_WF2,
4947 wakaba 1.52 }),
4948     ## TODO: Tests
4949     ## TODO: Tests for <nest/> in <output>
4950 wakaba 1.56 ## NOTE: "The output element should be used when ..." [WF2]
4951 wakaba 1.52 };
4952    
4953     $Element->{$HTML_NS}->{isindex} = {
4954     %HTMLEmptyChecker,
4955 wakaba 1.54 status => FEATURE_M12N10_REC_DEPRECATED |
4956     Whatpm::ContentChecker::FEATURE_DEPRECATED_SHOULD, ## [HTML4]
4957 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
4958     prompt => sub {}, ## NOTE: Text [M12N]
4959     }, {
4960     %HTMLAttrStatus,
4961     class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4962     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4963     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4964     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4965     prompt => FEATURE_M12N10_REC_DEPRECATED,
4966 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
4967 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4968 wakaba 1.52 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4969     }),
4970     ## TODO: Tests
4971     ## TODO: Tests for <nest/> in <isindex>
4972 wakaba 1.66 check_start => sub {
4973     my ($self, $item, $element_state) = @_;
4974    
4975     $element_state->{uri_info}->{action}->{type}->{action} = 1;
4976 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4977     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4978 wakaba 1.66 },
4979 wakaba 1.52 };
4980 wakaba 1.49
4981 wakaba 1.1 $Element->{$HTML_NS}->{script} = {
4982 wakaba 1.40 %HTMLChecker,
4983 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4984 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4985 wakaba 1.91 charset => sub {
4986     my ($self, $attr) = @_;
4987    
4988     unless ($attr->owner_element->has_attribute_ns (undef, 'src')) {
4989     $self->{onerror}->(type => 'attribute not allowed',
4990     node => $attr,
4991     level => $self->{must_level});
4992     }
4993    
4994     $HTMLCharsetChecker->($attr->value, @_);
4995     },
4996 wakaba 1.86 language => sub {}, ## NOTE: No syntax constraint according to HTML4.
4997 wakaba 1.91 src => $HTMLURIAttrChecker, ## TODO: pointed resource MUST be in type of type="" (resource error)
4998 wakaba 1.1 defer => $GetHTMLBooleanAttrChecker->('defer'),
4999     async => $GetHTMLBooleanAttrChecker->('async'),
5000 wakaba 1.91 type => $HTMLIMTAttrChecker, ## TODO: MUST NOT: |charset=""| parameter
5001 wakaba 1.49 }, {
5002     %HTMLAttrStatus,
5003     %HTMLM12NCommonAttrStatus,
5004 wakaba 1.50 async => FEATURE_HTML5_DEFAULT,
5005 wakaba 1.91 charset => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5006 wakaba 1.50 defer => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5007 wakaba 1.49 event => FEATURE_HTML4_REC_RESERVED,
5008     for => FEATURE_HTML4_REC_RESERVED,
5009 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5010 wakaba 1.49 language => FEATURE_M12N10_REC_DEPRECATED,
5011 wakaba 1.50 src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5012     type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5013 wakaba 1.9 }),
5014 wakaba 1.40 check_start => sub {
5015     my ($self, $item, $element_state) = @_;
5016 wakaba 1.1
5017 wakaba 1.40 if ($item->{node}->has_attribute_ns (undef, 'src')) {
5018     $element_state->{must_be_empty} = 1;
5019 wakaba 1.1 } else {
5020     ## NOTE: No content model conformance in HTML5 spec.
5021 wakaba 1.40 my $type = $item->{node}->get_attribute_ns (undef, 'type');
5022     my $language = $item->{node}->get_attribute_ns (undef, 'language');
5023 wakaba 1.1 if ((defined $type and $type eq '') or
5024     (defined $language and $language eq '')) {
5025     $type = 'text/javascript';
5026     } elsif (defined $type) {
5027     #
5028     } elsif (defined $language) {
5029     $type = 'text/' . $language;
5030     } else {
5031     $type = 'text/javascript';
5032     }
5033 wakaba 1.93
5034     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)]) {
5035     $type = "$1/$2";
5036     $type =~ tr/A-Z/a-z/; ## NOTE: ASCII case-insensitive
5037     ## TODO: Though we strip prameter here, it should not be ignored for the purpose of conformance checking...
5038     }
5039     $element_state->{script_type} = $type;
5040 wakaba 1.40 }
5041 wakaba 1.66
5042     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
5043 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5044     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5045 wakaba 1.40 },
5046     check_child_element => sub {
5047     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5048     $child_is_transparent, $element_state) = @_;
5049     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5050     $self->{onerror}->(node => $child_el,
5051     type => 'element not allowed:minus',
5052     level => $self->{must_level});
5053     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5054     #
5055     } else {
5056     if ($element_state->{must_be_empty}) {
5057     $self->{onerror}->(node => $child_el,
5058     type => 'element not allowed');
5059     }
5060     }
5061     },
5062     check_child_text => sub {
5063     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5064     if ($has_significant and
5065     $element_state->{must_be_empty}) {
5066     $self->{onerror}->(node => $child_node,
5067     type => 'character not allowed');
5068     }
5069     },
5070     check_end => sub {
5071     my ($self, $item, $element_state) = @_;
5072     unless ($element_state->{must_be_empty}) {
5073 wakaba 1.93 if ($element_state->{script_type} =~ m![+/][Xx][Mm][Ll]\z!) {
5074     ## NOTE: XML content should be checked by THIS instance of checker
5075     ## as part of normal tree validation.
5076     #
5077     } else {
5078     $self->{onsubdoc}->({s => $element_state->{text},
5079     container_node => $item->{node},
5080     media_type => $element_state->{script_type},
5081     is_char_string => 1});
5082     ## ISSUE: Should we raise some kind of error for
5083     ## <script type="text/xml">aaaaa</script>?
5084     }
5085    
5086     #$self->{onerror}->(node => $item->{node}, level => 'unsupported',
5087     # type => 'script:'.$element_state->{script_type});
5088 wakaba 1.40 ## TODO: text/javascript support
5089    
5090     $HTMLChecker{check_end}->(@_);
5091 wakaba 1.1 }
5092     },
5093 wakaba 1.91 ## TODO: There MUST be |type| unless the script type is JavaScript. (resource error)
5094     ## NOTE: "When used to include script data, the script data must be embedded
5095     ## inline, the format of the data must be given using the type attribute,
5096     ## and the src attribute must not be specified." - not testable.
5097     ## TODO: It would be possible to err <script type=text/plain src=...>
5098 wakaba 1.1 };
5099 wakaba 1.25 ## ISSUE: Significant check and text child node
5100 wakaba 1.1
5101     ## NOTE: When script is disabled.
5102     $Element->{$HTML_NS}->{noscript} = {
5103 wakaba 1.40 %HTMLTransparentChecker,
5104 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5105     check_attrs => $GetHTMLAttrsChecker->({}, {
5106     %HTMLAttrStatus,
5107     %HTMLM12NCommonAttrStatus,
5108 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5109 wakaba 1.49 }),
5110 wakaba 1.40 check_start => sub {
5111     my ($self, $item, $element_state) = @_;
5112 wakaba 1.3
5113 wakaba 1.40 unless ($item->{node}->owner_document->manakai_is_html) {
5114     $self->{onerror}->(node => $item->{node}, type => 'in XML:noscript');
5115 wakaba 1.3 }
5116    
5117 wakaba 1.40 unless ($self->{flag}->{in_head}) {
5118     $self->_add_minus_elements ($element_state,
5119     {$HTML_NS => {noscript => 1}});
5120     }
5121 wakaba 1.79
5122     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5123     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5124 wakaba 1.3 },
5125 wakaba 1.40 check_child_element => sub {
5126     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5127     $child_is_transparent, $element_state) = @_;
5128     if ($self->{flag}->{in_head}) {
5129     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5130     $self->{onerror}->(node => $child_el,
5131     type => 'element not allowed:minus',
5132     level => $self->{must_level});
5133     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5134     #
5135     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'link') {
5136     #
5137     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') {
5138     if ($child_el->has_attribute_ns (undef, 'scoped')) {
5139     $self->{onerror}->(node => $child_el,
5140     type => 'element not allowed:head noscript',
5141     level => $self->{must_level});
5142     }
5143     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'meta') {
5144 wakaba 1.47 my $http_equiv_attr
5145     = $child_el->get_attribute_node_ns (undef, 'http-equiv');
5146     if ($http_equiv_attr) {
5147     ## TODO: case
5148     if (lc $http_equiv_attr->value eq 'content-type') {
5149 wakaba 1.40 $self->{onerror}->(node => $child_el,
5150 wakaba 1.34 type => 'element not allowed:head noscript',
5151     level => $self->{must_level});
5152 wakaba 1.47 } else {
5153     #
5154 wakaba 1.3 }
5155 wakaba 1.47 } else {
5156     $self->{onerror}->(node => $child_el,
5157     type => 'element not allowed:head noscript',
5158     level => $self->{must_level});
5159 wakaba 1.3 }
5160 wakaba 1.40 } else {
5161     $self->{onerror}->(node => $child_el,
5162     type => 'element not allowed:head noscript',
5163     level => $self->{must_level});
5164     }
5165     } else {
5166     $HTMLTransparentChecker{check_child_element}->(@_);
5167     }
5168     },
5169     check_child_text => sub {
5170     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5171     if ($self->{flag}->{in_head}) {
5172     if ($has_significant) {
5173     $self->{onerror}->(node => $child_node,
5174     type => 'character not allowed');
5175 wakaba 1.3 }
5176     } else {
5177 wakaba 1.40 $HTMLTransparentChecker{check_child_text}->(@_);
5178     }
5179     },
5180     check_end => sub {
5181     my ($self, $item, $element_state) = @_;
5182     $self->_remove_minus_elements ($element_state);
5183     if ($self->{flag}->{in_head}) {
5184     $HTMLChecker{check_end}->(@_);
5185     } else {
5186     $HTMLPhrasingContentChecker{check_end}->(@_);
5187 wakaba 1.3 }
5188 wakaba 1.1 },
5189     };
5190 wakaba 1.3 ## ISSUE: Scripting is disabled: <head><noscript><html a></noscript></head>
5191 wakaba 1.1
5192     $Element->{$HTML_NS}->{'event-source'} = {
5193 wakaba 1.40 %HTMLEmptyChecker,
5194 wakaba 1.48 status => FEATURE_HTML5_LC,
5195 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5196 wakaba 1.1 src => $HTMLURIAttrChecker,
5197 wakaba 1.50 }, {
5198     %HTMLAttrStatus,
5199     src => FEATURE_HTML5_LC,
5200 wakaba 1.1 }),
5201 wakaba 1.66 check_start => sub {
5202     my ($self, $item, $element_state) = @_;
5203    
5204     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
5205 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5206     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5207 wakaba 1.66 },
5208 wakaba 1.1 };
5209    
5210     $Element->{$HTML_NS}->{details} = {
5211 wakaba 1.72 %HTMLFlowContentChecker,
5212 wakaba 1.48 status => FEATURE_HTML5_WD,
5213 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5214 wakaba 1.1 open => $GetHTMLBooleanAttrChecker->('open'),
5215 wakaba 1.50 }, {
5216     %HTMLAttrStatus,
5217 wakaba 1.59 open => FEATURE_HTML5_WD,
5218 wakaba 1.1 }),
5219 wakaba 1.72 ## NOTE: legend, Flow
5220 wakaba 1.43 check_child_element => sub {
5221     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5222     $child_is_transparent, $element_state) = @_;
5223     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5224     $self->{onerror}->(node => $child_el,
5225     type => 'element not allowed:minus',
5226     level => $self->{must_level});
5227     $element_state->{has_non_legend} = 1;
5228     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5229     #
5230     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') {
5231     if ($element_state->{has_non_legend}) {
5232     $self->{onerror}->(node => $child_el,
5233     type => 'element not allowed:details legend',
5234     level => $self->{must_level});
5235     }
5236     $element_state->{has_legend} = 1;
5237     $element_state->{has_non_legend} = 1;
5238     } else {
5239 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
5240 wakaba 1.43 $element_state->{has_non_legend} = 1 unless $child_is_transparent;
5241     ## ISSUE: |<details><object><legend>xx</legend></object>..</details>|
5242     ## is conforming?
5243     }
5244     },
5245     check_child_text => sub {
5246     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5247     if ($has_significant) {
5248     $element_state->{has_non_legend} = 1;
5249     }
5250     },
5251     check_end => sub {
5252     my ($self, $item, $element_state) = @_;
5253 wakaba 1.1
5254 wakaba 1.43 unless ($element_state->{has_legend}) {
5255     $self->{onerror}->(node => $item->{node},
5256     type => 'element missing:legend',
5257     level => $self->{must_level});
5258     }
5259    
5260 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
5261 wakaba 1.43 ## ISSUE: |<details><legend>aa</legend></details>| error?
5262 wakaba 1.1 },
5263     };
5264    
5265     $Element->{$HTML_NS}->{datagrid} = {
5266 wakaba 1.72 %HTMLFlowContentChecker,
5267 wakaba 1.48 status => FEATURE_HTML5_WD,
5268 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5269 wakaba 1.1 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5270     multiple => $GetHTMLBooleanAttrChecker->('multiple'),
5271 wakaba 1.50 }, {
5272     %HTMLAttrStatus,
5273     disabled => FEATURE_HTML5_WD,
5274     multiple => FEATURE_HTML5_WD,
5275 wakaba 1.1 }),
5276 wakaba 1.40 check_start => sub {
5277     my ($self, $item, $element_state) = @_;
5278 wakaba 1.1
5279 wakaba 1.40 $self->_add_minus_elements ($element_state,
5280     {$HTML_NS => {a => 1, datagrid => 1}});
5281     $element_state->{phase} = 'any';
5282 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5283     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5284 wakaba 1.40 },
5285 wakaba 1.95 ## NOTE: Flow -(text* (table|select|datalist) Flow*) | table | select |
5286     ## datalist | Empty
5287 wakaba 1.40 check_child_element => sub {
5288     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5289     $child_is_transparent, $element_state) = @_;
5290     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5291     $self->{onerror}->(node => $child_el,
5292     type => 'element not allowed:minus',
5293     level => $self->{must_level});
5294     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5295     #
5296 wakaba 1.72 } elsif ($element_state->{phase} eq 'flow') {
5297     if ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) {
5298 wakaba 1.44 if (not $element_state->{has_element} and
5299 wakaba 1.40 $child_nsuri eq $HTML_NS and
5300 wakaba 1.95 {
5301     table => 1, select => 1, datalist => 1,
5302     }->{$child_ln}) {
5303 wakaba 1.40 $self->{onerror}->(node => $child_el,
5304     type => 'element not allowed');
5305     } else {
5306 wakaba 1.8 #
5307 wakaba 1.1 }
5308 wakaba 1.40 } else {
5309     $self->{onerror}->(node => $child_el,
5310     type => 'element not allowed');
5311     }
5312 wakaba 1.43 $element_state->{has_element} = 1;
5313 wakaba 1.40 } elsif ($element_state->{phase} eq 'any') {
5314     if ($child_nsuri eq $HTML_NS and
5315     {table => 1, select => 1, datalist => 1}->{$child_ln}) {
5316     $element_state->{phase} = 'none';
5317 wakaba 1.72 } elsif ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) {
5318 wakaba 1.40 $element_state->{has_element} = 1;
5319 wakaba 1.72 $element_state->{phase} = 'flow';
5320 wakaba 1.40 } else {
5321     $self->{onerror}->(node => $child_el,
5322     type => 'element not allowed');
5323     }
5324     } elsif ($element_state->{phase} eq 'none') {
5325     $self->{onerror}->(node => $child_el,
5326     type => 'element not allowed');
5327     } else {
5328     die "check_child_element: Bad |datagrid| phase: $element_state->{phase}";
5329     }
5330     },
5331     check_child_text => sub {
5332     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5333     if ($has_significant) {
5334 wakaba 1.72 if ($element_state->{phase} eq 'flow') {
5335 wakaba 1.40 #
5336     } elsif ($element_state->{phase} eq 'any') {
5337 wakaba 1.72 $element_state->{phase} = 'flow';
5338 wakaba 1.40 } else {
5339     $self->{onerror}->(node => $child_node,
5340     type => 'character not allowed');
5341 wakaba 1.1 }
5342     }
5343 wakaba 1.40 },
5344     check_end => sub {
5345     my ($self, $item, $element_state) = @_;
5346     $self->_remove_minus_elements ($element_state);
5347 wakaba 1.1
5348 wakaba 1.95 if ($element_state->{phase} eq 'flow') {
5349     if ($element_state->{has_significant}) {
5350     $item->{real_parent_state}->{has_significant} = 1;
5351     } elsif ($item->{transparent}) {
5352     #
5353     } else {
5354     $self->{onerror}->(node => $item->{node},
5355     level => $self->{should_level},
5356     type => 'no significant content');
5357     }
5358     } else {
5359     ## NOTE: Since the content model explicitly allows a |datagird| element
5360     ## being empty, we don't raise "no significant content" error for this
5361     ## element when there is no element. (We should raise an error for
5362     ## |<datagrid><br></datagrid>|, however.)
5363     ## NOTE: As a side-effect, when the |datagrid| element only contains
5364     ## non-conforming content, then the |phase| flag has not changed from
5365     ## |any|, no "no significant content" error is raised neither.
5366     ## NOTE: Another side-effect of the current implementation:
5367     ## |<daragrid><datagrid/></datagrid>| has no "no significant content"
5368     ## error at all.
5369 wakaba 1.40 $HTMLChecker{check_end}->(@_);
5370     }
5371     },
5372 wakaba 1.1 };
5373    
5374     $Element->{$HTML_NS}->{command} = {
5375 wakaba 1.40 %HTMLEmptyChecker,
5376 wakaba 1.48 status => FEATURE_HTML5_WD,
5377 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5378 wakaba 1.1 checked => $GetHTMLBooleanAttrChecker->('checked'),
5379     default => $GetHTMLBooleanAttrChecker->('default'),
5380     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5381     hidden => $GetHTMLBooleanAttrChecker->('hidden'),
5382     icon => $HTMLURIAttrChecker,
5383     label => sub { }, ## NOTE: No conformance creteria
5384     radiogroup => sub { }, ## NOTE: No conformance creteria
5385     type => sub {
5386     my ($self, $attr) = @_;
5387     my $value = $attr->value;
5388     unless ({command => 1, checkbox => 1, radio => 1}->{$value}) {
5389     $self->{onerror}->(node => $attr, type => 'attribute value not allowed');
5390     }
5391     },
5392 wakaba 1.50 }, {
5393     %HTMLAttrStatus,
5394     checked => FEATURE_HTML5_WD,
5395     default => FEATURE_HTML5_WD,
5396     disabled => FEATURE_HTML5_WD,
5397     hidden => FEATURE_HTML5_WD,
5398     icon => FEATURE_HTML5_WD,
5399     label => FEATURE_HTML5_WD,
5400     radiogroup => FEATURE_HTML5_WD,
5401     type => FEATURE_HTML5_WD,
5402 wakaba 1.1 }),
5403 wakaba 1.66 check_start => sub {
5404     my ($self, $item, $element_state) = @_;
5405    
5406     $element_state->{uri_info}->{icon}->{type}->{embedded} = 1;
5407 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5408     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5409 wakaba 1.66 },
5410 wakaba 1.1 };
5411    
5412     $Element->{$HTML_NS}->{menu} = {
5413 wakaba 1.40 %HTMLPhrasingContentChecker,
5414 wakaba 1.54 #status => FEATURE_M12N10_REC_DEPRECATED | FEATURE_HTML5_WD,
5415     status => FEATURE_M12N10_REC | FEATURE_HTML5_WD,
5416     ## NOTE: We don't want any |menu| element warned as deprecated.
5417 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5418 wakaba 1.1 autosubmit => $GetHTMLBooleanAttrChecker->('autosubmit'),
5419 wakaba 1.68 compact => $GetHTMLBooleanAttrChecker->('compact'),
5420 wakaba 1.1 id => sub {
5421     ## NOTE: same as global |id=""|, with |$self->{menu}| registeration
5422     my ($self, $attr) = @_;
5423     my $value = $attr->value;
5424     if (length $value > 0) {
5425     if ($self->{id}->{$value}) {
5426     $self->{onerror}->(node => $attr, type => 'duplicate ID');
5427     push @{$self->{id}->{$value}}, $attr;
5428     } else {
5429     $self->{id}->{$value} = [$attr];
5430     }
5431     } else {
5432     ## NOTE: MUST contain at least one character
5433     $self->{onerror}->(node => $attr, type => 'empty attribute value');
5434     }
5435     if ($value =~ /[\x09-\x0D\x20]/) {
5436     $self->{onerror}->(node => $attr, type => 'space in ID');
5437     }
5438     $self->{menu}->{$value} ||= $attr;
5439     ## ISSUE: <menu id=""><p contextmenu=""> match?
5440     },
5441     label => sub { }, ## NOTE: No conformance creteria
5442     type => $GetHTMLEnumeratedAttrChecker->({context => 1, toolbar => 1}),
5443 wakaba 1.49 }, {
5444     %HTMLAttrStatus,
5445     %HTMLM12NCommonAttrStatus,
5446 wakaba 1.61 align => FEATURE_HTML2X_RFC,
5447 wakaba 1.50 autosubmit => FEATURE_HTML5_WD,
5448 wakaba 1.49 compat => FEATURE_M12N10_REC_DEPRECATED,
5449 wakaba 1.50 label => FEATURE_HTML5_WD,
5450     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5451 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
5452     sdapref => FEATURE_HTML20_RFC,
5453 wakaba 1.50 type => FEATURE_HTML5_WD,
5454 wakaba 1.1 }),
5455 wakaba 1.40 check_start => sub {
5456     my ($self, $item, $element_state) = @_;
5457     $element_state->{phase} = 'li or phrasing';
5458     $element_state->{in_menu_original} = $self->{flag}->{in_menu};
5459     $self->{flag}->{in_menu} = 1;
5460 wakaba 1.79
5461     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5462     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5463 wakaba 1.40 },
5464     check_child_element => sub {
5465     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5466     $child_is_transparent, $element_state) = @_;
5467     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5468     $self->{onerror}->(node => $child_el,
5469     type => 'element not allowed:minus',
5470     level => $self->{must_level});
5471     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5472     #
5473     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'li') {
5474     if ($element_state->{phase} eq 'li') {
5475     #
5476     } elsif ($element_state->{phase} eq 'li or phrasing') {
5477     $element_state->{phase} = 'li';
5478     } else {
5479     $self->{onerror}->(node => $child_el, type => 'element not allowed');
5480     }
5481     } elsif ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
5482     if ($element_state->{phase} eq 'phrasing') {
5483     #
5484     } elsif ($element_state->{phase} eq 'li or phrasing') {
5485     $element_state->{phase} = 'phrasing';
5486     } else {
5487     $self->{onerror}->(node => $child_el, type => 'element not allowed');
5488     }
5489     } else {
5490     $self->{onerror}->(node => $child_el, type => 'element not allowed');
5491     }
5492     },
5493     check_child_text => sub {
5494     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5495     if ($has_significant) {
5496     if ($element_state->{phase} eq 'phrasing') {
5497     #
5498     } elsif ($element_state->{phase} eq 'li or phrasing') {
5499     $element_state->{phase} = 'phrasing';
5500     } else {
5501     $self->{onerror}->(node => $child_node,
5502     type => 'character not allowed');
5503 wakaba 1.1 }
5504     }
5505 wakaba 1.40 },
5506     check_end => sub {
5507     my ($self, $item, $element_state) = @_;
5508     delete $self->{flag}->{in_menu} unless $element_state->{in_menu_original};
5509    
5510     if ($element_state->{phase} eq 'li') {
5511     $HTMLChecker{check_end}->(@_);
5512     } else { # 'phrasing' or 'li or phrasing'
5513     $HTMLPhrasingContentChecker{check_end}->(@_);
5514 wakaba 1.1 }
5515     },
5516 wakaba 1.8 };
5517    
5518     $Element->{$HTML_NS}->{datatemplate} = {
5519 wakaba 1.40 %HTMLChecker,
5520 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
5521 wakaba 1.40 check_child_element => sub {
5522     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5523     $child_is_transparent, $element_state) = @_;
5524     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5525     $self->{onerror}->(node => $child_el,
5526     type => 'element not allowed:minus',
5527     level => $self->{must_level});
5528     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5529     #
5530     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'rule') {
5531     #
5532     } else {
5533     $self->{onerror}->(node => $child_el,
5534     type => 'element not allowed:datatemplate');
5535     }
5536     },
5537     check_child_text => sub {
5538     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5539     if ($has_significant) {
5540     $self->{onerror}->(node => $child_node, type => 'character not allowed');
5541 wakaba 1.8 }
5542     },
5543     is_xml_root => 1,
5544     };
5545    
5546     $Element->{$HTML_NS}->{rule} = {
5547 wakaba 1.40 %HTMLChecker,
5548 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
5549 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5550 wakaba 1.23 condition => $HTMLSelectorsAttrChecker,
5551 wakaba 1.92 mode => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(),
5552 wakaba 1.50 }, {
5553     %HTMLAttrStatus,
5554     condition => FEATURE_HTML5_AT_RISK,
5555     mode => FEATURE_HTML5_AT_RISK,
5556 wakaba 1.8 }),
5557 wakaba 1.40 check_start => sub {
5558     my ($self, $item, $element_state) = @_;
5559 wakaba 1.79
5560 wakaba 1.40 $self->_add_plus_elements ($element_state, {$HTML_NS => {nest => 1}});
5561 wakaba 1.79 $element_state->{in_rule_original} = $self->{flag}->{in_rule};
5562     $self->{flag}->{in_rule} = 1;
5563    
5564     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5565     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5566 wakaba 1.40 },
5567     check_child_element => sub { },
5568     check_child_text => sub { },
5569     check_end => sub {
5570     my ($self, $item, $element_state) = @_;
5571 wakaba 1.79
5572 wakaba 1.40 $self->_remove_plus_elements ($element_state);
5573 wakaba 1.79 delete $self->{flag}->{in_rule} unless $element_state->{in_rule_original};
5574    
5575 wakaba 1.40 $HTMLChecker{check_end}->(@_);
5576 wakaba 1.8 },
5577     ## NOTE: "MAY be anything that, when the parent |datatemplate|
5578     ## is applied to some conforming data, results in a conforming DOM tree.":
5579     ## We don't check against this.
5580     };
5581    
5582     $Element->{$HTML_NS}->{nest} = {
5583 wakaba 1.40 %HTMLEmptyChecker,
5584 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
5585 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5586 wakaba 1.23 filter => $HTMLSelectorsAttrChecker,
5587     mode => sub {
5588     my ($self, $attr) = @_;
5589     my $value = $attr->value;
5590     if ($value !~ /\A[^\x09-\x0D\x20]+\z/) {
5591     $self->{onerror}->(node => $attr, type => 'mode:syntax error');
5592     }
5593     },
5594 wakaba 1.50 }, {
5595     %HTMLAttrStatus,
5596     filter => FEATURE_HTML5_AT_RISK,
5597     mode => FEATURE_HTML5_AT_RISK,
5598 wakaba 1.8 }),
5599 wakaba 1.1 };
5600    
5601     $Element->{$HTML_NS}->{legend} = {
5602 wakaba 1.40 %HTMLPhrasingContentChecker,
5603 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5604 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5605 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5606 wakaba 1.52 # align => $GetHTMLEnumeratedAttrChecker->({
5607     # top => 1, bottom => 1, left => 1, right => 1,
5608     # }),
5609     }, {
5610 wakaba 1.49 %HTMLAttrStatus,
5611     %HTMLM12NCommonAttrStatus,
5612     accesskey => FEATURE_M12N10_REC,
5613     align => FEATURE_M12N10_REC_DEPRECATED,
5614 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5615 wakaba 1.49 }),
5616 wakaba 1.1 };
5617    
5618     $Element->{$HTML_NS}->{div} = {
5619 wakaba 1.72 %HTMLFlowContentChecker,
5620 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5621 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
5622     align => $GetHTMLEnumeratedAttrChecker->({
5623     left => 1, center => 1, right => 1, justify => 1,
5624     }),
5625     }, {
5626 wakaba 1.49 %HTMLAttrStatus,
5627 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5628 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
5629     datafld => FEATURE_HTML4_REC_RESERVED,
5630     dataformatas => FEATURE_HTML4_REC_RESERVED,
5631     datasrc => FEATURE_HTML4_REC_RESERVED,
5632 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5633 wakaba 1.49 }),
5634 wakaba 1.66 check_start => sub {
5635     my ($self, $item, $element_state) = @_;
5636    
5637     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
5638 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5639     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5640 wakaba 1.66 },
5641 wakaba 1.1 };
5642    
5643 wakaba 1.64 $Element->{$HTML_NS}->{center} = {
5644 wakaba 1.72 %HTMLFlowContentChecker,
5645 wakaba 1.64 status => FEATURE_M12N10_REC_DEPRECATED,
5646     check_attrs => $GetHTMLAttrsChecker->({}, {
5647     %HTMLAttrStatus,
5648     %HTMLM12NCommonAttrStatus,
5649     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5650     }),
5651     };
5652    
5653 wakaba 1.1 $Element->{$HTML_NS}->{font} = {
5654 wakaba 1.40 %HTMLTransparentChecker,
5655 wakaba 1.78 status => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC_DEPRECATED,
5656 wakaba 1.70 check_attrs => $GetHTMLAttrsChecker->({
5657     ## TODO: HTML4 |size|, |color|, |face|
5658 wakaba 1.49 }, {
5659     %HTMLAttrStatus,
5660 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5661 wakaba 1.49 color => FEATURE_M12N10_REC_DEPRECATED,
5662 wakaba 1.50 dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5663 wakaba 1.49 face => FEATURE_M12N10_REC_DEPRECATED,
5664 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5665     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5666 wakaba 1.49 size => FEATURE_M12N10_REC_DEPRECATED,
5667 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5668 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5669 wakaba 1.49 }),
5670 wakaba 1.78 ## NOTE: When the |font| element was defined in the HTML5 specification,
5671     ## it is allowed only in a document with the WYSIWYG signature. The
5672     ## checker does not check whether there is the signature, since the
5673     ## signature is dropped, too, and has never been implemented. (In addition,
5674     ## for any |font| element an "element not defined" error is raised anyway,
5675     ## such that we don't have to raise an additional error.)
5676 wakaba 1.1 };
5677 wakaba 1.49
5678 wakaba 1.64 $Element->{$HTML_NS}->{basefont} = {
5679     %HTMLEmptyChecker,
5680     status => FEATURE_M12N10_REC_DEPRECATED,
5681     check_attrs => $GetHTMLAttrsChecker->({
5682     ## TODO: color, face, size
5683     }, {
5684     %HTMLAttrStatus,
5685     color => FEATURE_M12N10_REC_DEPRECATED,
5686     face => FEATURE_M12N10_REC_DEPRECATED,
5687     #id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
5688     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5689     size => FEATURE_M12N10_REC_DEPRECATED,
5690     }),
5691     };
5692    
5693 wakaba 1.49 ## TODO: frameset FEATURE_M12N10_REC
5694     ## class title id cols rows onload onunload style(x10)
5695     ## frame frameborder longdesc marginheight marginwidth noresize scrolling src name(deprecated) class,id,title,style(x10)
5696     ## noframes Common, lang(xhtml10)
5697    
5698 wakaba 1.82 ## TODO: CR: ruby rb rt rp rbc rtc @rbspan (M12NXHTML2Common)
5699 wakaba 1.56
5700 wakaba 1.61 ## TODO: xmp, listing, plaintext FEATURE_HTML32_REC_OBSOLETE
5701     ## TODO: ^^^ lang, dir, id, class [HTML 2.x] sdaform [HTML 2.0]
5702     ## xmp, listing sdapref[HTML2,0]
5703    
5704 wakaba 1.56 =pod
5705    
5706     WF2: Documents MUST comply to [CHARMOD].
5707     WF2: Vencor extensions MUST NOT be used.
5708    
5709 wakaba 1.61 HTML 2.0 nextid @n
5710    
5711     RFC 2659: CERTS CRYPTOPTS
5712    
5713     ISO-HTML: pre-html, divN
5714 wakaba 1.82
5715     XHTML2: blockcode (Common), h (Common), separator (Common), l (Common),
5716     di (Common), nl (Common), handler (Common, type), standby (Common),
5717     summary (Common)
5718    
5719     Access & XHTML2: access
5720    
5721     XML Events & XForms (for XHTML2 support; very, very low priority)
5722 wakaba 1.61
5723 wakaba 1.56 =cut
5724 wakaba 1.61
5725     ## NOTE: Where RFC 2659 allows additional attributes is unclear.
5726     ## We added them only to |a|. |link| and |form| might also allow them
5727     ## in theory.
5728 wakaba 1.1
5729     $Whatpm::ContentChecker::Namespace->{$HTML_NS}->{loaded} = 1;
5730    
5731     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24