/[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.127 - (hide annotations) (download)
Fri Sep 12 03:33:42 2008 UTC (16 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.126: +1 -0 lines
++ whatpm/Whatpm/ContentChecker/ChangeLog	12 Sep 2008 03:33:40 -0000
	* HTML.pm: Tentative support for |textarea| |oninput|
	attribute.

2008-09-12  Wakaba  <wakaba@suika.fam.cx>

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24