/[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.111 - (hide annotations) (download)
Sat Aug 30 12:33:36 2008 UTC (16 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.110: +38 -4 lines
++ whatpm/t/ChangeLog	30 Aug 2008 12:33:27 -0000
	* content-model-1.dat, content-model-2.dat: Test data updated
	and added for HTML5 revision 2062 and so on (@lang and @xml:lang).

2008-08-30  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/ChangeLog	30 Aug 2008 12:30:55 -0000
	* ContentChecker.pm: |xml:lang| attribute value must be same
	as |lang| attribute value for HTML elements (HTML5 revision 2062
	and so on).

2008-08-30  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/ContentChecker/ChangeLog	30 Aug 2008 12:32:39 -0000
	* HTML.pm: |xml:lang| attribute value must be same
	as |lang| attribute value for HTML elements (HTML5 revision 2062
	and so on).  |lang| attribute in XML document is no longer
	disallowed (HTML5 revision 2062).  |xml:lang| is allowed
	in HTML document (HTML5 revision 2062 and so on).

2008-08-30  Wakaba  <wakaba@suika.fam.cx>

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24