/[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.129 - (hide annotations) (download)
Sat Sep 20 02:19:49 2008 UTC (16 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.128: +47 -3 lines
++ whatpm/t/ChangeLog	20 Sep 2008 02:17:40 -0000
2008-09-20  Wakaba  <wakaba@suika.fam.cx>

	* content-model-2.dat: Add test data for accept-charset=""
	attributes.

++ whatpm/Whatpm/ContentChecker/ChangeLog	20 Sep 2008 02:19:00 -0000
2008-09-20  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm ($HTMLCharsetChecker): Support for ASCII-compatibility
	check.
	($HTMLCharsetsAttrChecker): New checker for accept-charset=""
	support.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24