/[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.115 - (hide annotations) (download)
Sun Aug 31 06:57:32 2008 UTC (16 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.114: +20 -7 lines
++ whatpm/t/ChangeLog	31 Aug 2008 06:54:57 -0000
	* content-model-1.dat, content-model-2.dat: Test data
	for the |bb| element are added (cf. HTML5 revision 1894).

	* content-model-2.dat: |irrelevant| renamed as |hidden| (HTML5
	revision 2119).

	* content-model-7.dat: New test file.

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	31 Aug 2008 06:55:32 -0000
	* HTML.pm: Support for the |bb| element (HTML5 revision 1894).
	|irrelevant| renamed as |hidden| (HTML5 revision 2119).

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24