/[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.117 - (hide annotations) (download)
Sun Aug 31 11:01:03 2008 UTC (16 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.116: +11 -23 lines
++ whatpm/t/ChangeLog	31 Aug 2008 10:51:47 -0000
	* content-model-2.dat, content-model-6.dat: Custom
	attributes on |embed| and |data-*| attributes must
	be XML compatible (HTML5 revision 1836).

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

	* content-model-2.dat: xmlns="" on HTML elements that are
	not the root of the HTML subtree are now allowed (HTML5
	revision 1834).

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	31 Aug 2008 10:52:17 -0000
	* HTML.pm: |embed| custom attributes and |data-*| attributes
	must be XML compatible (HTML5 revision 1836).

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

	* HTML.pm: All HTML elements can have xmlns="" attributes (HTML5
	revision 1834).

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

1 wakaba 1.1 package Whatpm::ContentChecker;
2     use strict;
3     require Whatpm::ContentChecker;
4    
5 wakaba 1.117 use Char::Class::XML qw/InXML_NCNameStartChar10 InXMLNCNameChar10/;
6    
7 wakaba 1.1 my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
8    
9 wakaba 1.58 sub FEATURE_HTML5_ROLE () {
10     Whatpm::ContentChecker::FEATURE_STATUS_WD
11     ## TODO: svg:*/@role
12     }
13    
14 wakaba 1.89 sub FEATURE_HTML5_COMPLETE () {
15     Whatpm::ContentChecker::FEATURE_STATUS_REC |
16     Whatpm::ContentChecker::FEATURE_ALLOWED
17     }
18    
19 wakaba 1.54 sub FEATURE_HTML5_LC () {
20     Whatpm::ContentChecker::FEATURE_STATUS_LC |
21     Whatpm::ContentChecker::FEATURE_ALLOWED
22     }
23     sub FEATURE_HTML5_AT_RISK () {
24     Whatpm::ContentChecker::FEATURE_STATUS_WD |
25     Whatpm::ContentChecker::FEATURE_ALLOWED
26     }
27     sub FEATURE_HTML5_WD () {
28     Whatpm::ContentChecker::FEATURE_STATUS_WD |
29     Whatpm::ContentChecker::FEATURE_ALLOWED
30     }
31     sub FEATURE_HTML5_FD () {
32     Whatpm::ContentChecker::FEATURE_STATUS_WD |
33     Whatpm::ContentChecker::FEATURE_ALLOWED
34     }
35     sub FEATURE_HTML5_DEFAULT () {
36     Whatpm::ContentChecker::FEATURE_STATUS_WD |
37     Whatpm::ContentChecker::FEATURE_ALLOWED
38 wakaba 1.49 }
39 wakaba 1.54 sub FEATURE_HTML5_DROPPED () {
40     ## NOTE: Was part of HTML5, but was dropped.
41 wakaba 1.49 Whatpm::ContentChecker::FEATURE_STATUS_WD
42     }
43 wakaba 1.54 sub FEATURE_WF2 () {
44     Whatpm::ContentChecker::FEATURE_STATUS_LC |
45     Whatpm::ContentChecker::FEATURE_ALLOWED
46     }
47 wakaba 1.56 sub FEATURE_WF2_DEPRECATED () {
48     Whatpm::ContentChecker::FEATURE_STATUS_LC
49     ## NOTE: MUST NOT be used.
50     }
51 wakaba 1.49
52 wakaba 1.61 ## NOTE: Metainformation Attributes Module by W3C XHTML2 WG.
53     sub FEATURE_RDFA_LC () {
54     Whatpm::ContentChecker::FEATURE_STATUS_LC
55     }
56 wakaba 1.58
57 wakaba 1.82 sub FEATURE_RDFA_ED () {
58     Whatpm::ContentChecker::FEATURE_STATUS_WD
59     }
60    
61 wakaba 1.58 ## NOTE: XHTML Role LCWD has almost no information on how the |role|
62     ## attribute can be used- the only requirements for that matter is:
63     ## "the attribute MUST be referenced using its namespace-qualified form" (and
64     ## this is a host language conformance!).
65 wakaba 1.82 sub FEATURE_ROLE_LC () {
66     Whatpm::ContentChecker::FEATURE_STATUS_LC
67     }
68    
69     sub FEATURE_XHTML2_ED () {
70     Whatpm::ContentChecker::FEATURE_STATUS_WD
71     }
72 wakaba 1.58
73 wakaba 1.55 sub FEATURE_XHTMLBASIC11_CR () {
74     ## NOTE: Only additions to M12N10_REC are marked.
75     Whatpm::ContentChecker::FEATURE_STATUS_CR
76     }
77     sub FEATURE_XHTMLBASIC11_CR_DEPRECATED () {
78     Whatpm::ContentChecker::FEATURE_STATUS_CR |
79     Whatpm::ContentChecker::FEATURE_DEPRECATED_INFO
80     }
81    
82 wakaba 1.82 sub FEATURE_M12N11_LC () {
83     Whatpm::ContentChecker::FEATURE_STATUS_LC
84     }
85    
86 wakaba 1.99 sub FEATURE_RUBY_REC () {
87     Whatpm::ContentChecker::FEATURE_STATUS_CR
88     }
89    
90 wakaba 1.49 ## NOTE: M12N10 status is based on its abstract module definition.
91     ## It contains a number of problems. (However, again, it's a REC!)
92 wakaba 1.54 sub FEATURE_M12N10_REC () {
93     ## NOTE: Oh, XHTML m12n 1.0 passed the CR phase! W3C Process suck!
94     Whatpm::ContentChecker::FEATURE_STATUS_REC
95     }
96     sub FEATURE_M12N10_REC_DEPRECATED () {
97     Whatpm::ContentChecker::FEATURE_STATUS_REC |
98     Whatpm::ContentChecker::FEATURE_DEPRECATED_INFO
99     }
100 wakaba 1.58 ## NOTE: XHTML M12N 1.1 is a LC at the time of writing and no
101     ## addition from 1.0.
102 wakaba 1.49
103     ## NOTE: XHTML10 status is based on its transitional and frameset DTDs
104     ## (second edition). Only missing attributes from M12N10 abstract
105     ## definition are added.
106 wakaba 1.54 sub FEATURE_XHTML10_REC () {
107     Whatpm::ContentChecker::FEATURE_STATUS_CR
108     }
109    
110 wakaba 1.61 ## NOTE: Diff from HTML4.
111     sub FEATURE_ISOHTML_PREPARATION () { ## Informative documentation
112     Whatpm::ContentChecker::FEATURE_STATUS_CR
113     }
114 wakaba 1.58
115 wakaba 1.49 ## NOTE: HTML4 status is based on its transitional and frameset DTDs (HTML
116     ## 4.01). Only missing attributes from XHTML10 are added.
117 wakaba 1.54 sub FEATURE_HTML4_REC_RESERVED () {
118     Whatpm::ContentChecker::FEATURE_STATUS_WD
119     }
120    
121     ## TODO: According to HTML4 definition, authors SHOULD use style sheets
122     ## rather than presentational attributes (deprecated or not deprecated).
123 wakaba 1.48
124 wakaba 1.61 ## NOTE: Diff from HTML4.
125     sub FEATURE_HTML32_REC_OBSOLETE () {
126     Whatpm::ContentChecker::FEATURE_STATUS_CR |
127     Whatpm::ContentChecker::FEATURE_DEPRECATED_SHOULD
128     ## NOTE: Lowercase normative "should".
129     }
130    
131     sub FEATURE_RFC2659 () { ## Experimental RFC
132     Whatpm::ContentChecker::FEATURE_STATUS_CR
133     }
134    
135     ## NOTE: HTML 2.x - diff from HTML 2.0 and not in newer versions.
136     sub FEATURE_HTML2X_RFC () { ## Proposed Standard, obsolete
137     Whatpm::ContentChecker::FEATURE_STATUS_CR
138     }
139    
140     ## NOTE: Diff from HTML 2.0.
141     sub FEATURE_RFC1942 () { ## Experimental RFC, obsolete
142     Whatpm::ContentChecker::FEATURE_STATUS_CR
143     }
144    
145     ## NOTE: Diff from HTML 3.2.
146     sub FEATURE_HTML20_RFC () { ## Proposed Standard, obsolete
147     Whatpm::ContentChecker::FEATURE_STATUS_CR
148     }
149 wakaba 1.58
150 wakaba 1.29 ## December 2007 HTML5 Classification
151    
152     my $HTMLMetadataContent = {
153     $HTML_NS => {
154     title => 1, base => 1, link => 1, style => 1, script => 1, noscript => 1,
155     'event-source' => 1, command => 1, datatemplate => 1,
156     ## NOTE: A |meta| with no |name| element is not allowed as
157     ## a metadata content other than |head| element.
158     meta => 1,
159 wakaba 1.56 ## NOTE: Only when empty [WF2]
160     form => 1,
161 wakaba 1.29 },
162     ## NOTE: RDF is mentioned in the HTML5 spec.
163     ## TODO: Other RDF elements?
164     q<http://www.w3.org/1999/02/22-rdf-syntax-ns#> => {RDF => 1},
165     };
166    
167 wakaba 1.72 my $HTMLFlowContent = {
168 wakaba 1.29 $HTML_NS => {
169     section => 1, nav => 1, article => 1, blockquote => 1, aside => 1,
170     h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1, header => 1,
171     footer => 1, address => 1, p => 1, hr => 1, dialog => 1, pre => 1,
172     ol => 1, ul => 1, dl => 1, figure => 1, map => 1, table => 1,
173 wakaba 1.72 details => 1, ## ISSUE: "Flow element" in spec.
174     datagrid => 1, ## ISSUE: "Flow element" in spec.
175 wakaba 1.29 datatemplate => 1,
176     div => 1, ## ISSUE: No category in spec.
177     ## NOTE: |style| is only allowed if |scoped| attribute is specified.
178     ## Additionally, it must be before any other element or
179     ## non-inter-element-whitespace text node.
180     style => 1,
181    
182 wakaba 1.38 br => 1, q => 1, cite => 1, em => 1, strong => 1, small => 1, mark => 1,
183 wakaba 1.29 dfn => 1, abbr => 1, time => 1, progress => 1, meter => 1, code => 1,
184     var => 1, samp => 1, kbd => 1, sub => 1, sup => 1, span => 1, i => 1,
185 wakaba 1.99 b => 1, bdo => 1, ruby => 1,
186 wakaba 1.115 script => 1, noscript => 1, 'event-source' => 1, command => 1, bb => 1,
187 wakaba 1.29 a => 1,
188     datagrid => 1, ## ISSUE: "Interactive element" in the spec.
189     ## NOTE: |area| is allowed only as a descendant of |map|.
190     area => 1,
191    
192 wakaba 1.99 ## NOTE: Dropped
193     font => 1,
194    
195 wakaba 1.29 ins => 1, del => 1,
196    
197 wakaba 1.72 ## NOTE: If there is a |menu| ancestor, phrasing. Otherwise, flow.
198 wakaba 1.29 menu => 1,
199    
200     img => 1, iframe => 1, embed => 1, object => 1, video => 1, audio => 1,
201     canvas => 1,
202     },
203    
204     ## NOTE: Embedded
205     q<http://www.w3.org/1998/Math/MathML> => {math => 1},
206     q<http://www.w3.org/2000/svg> => {svg => 1},
207     };
208    
209 wakaba 1.58 my $HTMLSectioningContent = {
210 wakaba 1.57 $HTML_NS => {
211     section => 1, nav => 1, article => 1, aside => 1,
212     ## NOTE: |body| is only allowed in |html| element.
213     body => 1,
214     },
215     };
216    
217 wakaba 1.58 my $HTMLSectioningRoot = {
218 wakaba 1.29 $HTML_NS => {
219 wakaba 1.58 blockquote => 1, datagrid => 1, figure => 1, td => 1,
220 wakaba 1.29 },
221     };
222    
223     my $HTMLHeadingContent = {
224     $HTML_NS => {
225     h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1, header => 1,
226     },
227     };
228    
229     my $HTMLPhrasingContent = {
230 wakaba 1.72 ## NOTE: All phrasing content is also flow content.
231 wakaba 1.29 $HTML_NS => {
232 wakaba 1.38 br => 1, q => 1, cite => 1, em => 1, strong => 1, small => 1, mark => 1,
233 wakaba 1.29 dfn => 1, abbr => 1, time => 1, progress => 1, meter => 1, code => 1,
234     var => 1, samp => 1, kbd => 1, sub => 1, sup => 1, span => 1, i => 1,
235 wakaba 1.99 b => 1, bdo => 1, ruby => 1,
236 wakaba 1.115 script => 1, noscript => 1, 'event-source' => 1, command => 1, bb => 1,
237 wakaba 1.29 a => 1,
238     datagrid => 1, ## ISSUE: "Interactive element" in the spec.
239     ## NOTE: |area| is allowed only as a descendant of |map|.
240     area => 1,
241    
242 wakaba 1.99 ## NOTE: Dropped
243     font => 1,
244    
245 wakaba 1.29 ## NOTE: Transparent.
246     ins => 1, del => 1,
247    
248 wakaba 1.72 ## NOTE: If there is a |menu| ancestor, phrasing. Otherwise, flow.
249 wakaba 1.29 menu => 1,
250    
251     img => 1, iframe => 1, embed => 1, object => 1, video => 1, audio => 1,
252     canvas => 1,
253 wakaba 1.56
254     ## NOTE: WF2
255     input => 1, ## NOTE: type=hidden
256     datalist => 1, ## NOTE: block | where |select| allowed
257 wakaba 1.29 },
258    
259     ## NOTE: Embedded
260     q<http://www.w3.org/1998/Math/MathML> => {math => 1},
261     q<http://www.w3.org/2000/svg> => {svg => 1},
262    
263     ## NOTE: And non-inter-element-whitespace text nodes.
264     };
265    
266 wakaba 1.40 ## $HTMLEmbeddedContent: See Whatpm::ContentChecker.
267 wakaba 1.29
268     my $HTMLInteractiveContent = {
269     $HTML_NS => {
270     a => 1,
271 wakaba 1.36 datagrid => 1, ## ISSUE: Categorized as "Inetractive element"
272 wakaba 1.115 bb => 1,
273 wakaba 1.29 },
274     };
275    
276 wakaba 1.36 ## NOTE: $HTMLTransparentElements: See Whatpm::ContentChecker.
277     ## NOTE: Semi-transparent elements: See Whatpm::ContentChecker.
278    
279     ## -- Common attribute syntacx checkers
280    
281 wakaba 1.1 our $AttrChecker;
282 wakaba 1.82 our $AttrStatus;
283 wakaba 1.1
284     my $GetHTMLEnumeratedAttrChecker = sub {
285     my $states = shift; # {value => conforming ? 1 : -1}
286     return sub {
287     my ($self, $attr) = @_;
288     my $value = lc $attr->value; ## TODO: ASCII case insensitibility?
289     if ($states->{$value} > 0) {
290     #
291     } elsif ($states->{$value}) {
292 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'enumerated:non-conforming',
293     level => $self->{level}->{must});
294 wakaba 1.1 } else {
295 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'enumerated:invalid',
296     level => $self->{level}->{must});
297 wakaba 1.1 }
298     };
299     }; # $GetHTMLEnumeratedAttrChecker
300    
301     my $GetHTMLBooleanAttrChecker = sub {
302     my $local_name = shift;
303     return sub {
304     my ($self, $attr) = @_;
305 wakaba 1.88 my $value = lc $attr->value; ## TODO: case
306 wakaba 1.1 unless ($value eq $local_name or $value eq '') {
307 wakaba 1.88 $self->{onerror}->(node => $attr, type => 'boolean:invalid',
308 wakaba 1.104 level => $self->{level}->{must});
309 wakaba 1.1 }
310     };
311     }; # $GetHTMLBooleanAttrChecker
312    
313 wakaba 1.8 ## Unordered set of space-separated tokens
314 wakaba 1.92 my $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker = sub {
315     my $allowed_words = shift;
316     return sub {
317     my ($self, $attr) = @_;
318     my %word;
319 wakaba 1.96 for my $word (grep {length $_} split /[\x09-\x0D\x20]+/, $attr->value) {
320 wakaba 1.92 unless ($word{$word}) {
321     $word{$word} = 1;
322     if (not defined $allowed_words or
323     $allowed_words->{$word}) {
324     #
325     } else {
326 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'word not allowed',
327 wakaba 1.92 value => $word,
328 wakaba 1.104 level => $self->{level}->{must});
329 wakaba 1.92 }
330     } else {
331 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'duplicate token',
332     value => $word,
333     level => $self->{level}->{must});
334 wakaba 1.92 }
335 wakaba 1.8 }
336 wakaba 1.92 };
337     }; # $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker
338 wakaba 1.8
339 wakaba 1.1 ## |rel| attribute (unordered set of space separated tokens,
340     ## whose allowed values are defined by the section on link types)
341     my $HTMLLinkTypesAttrChecker = sub {
342 wakaba 1.66 my ($a_or_area, $todo, $self, $attr, $item, $element_state) = @_;
343 wakaba 1.1 my %word;
344 wakaba 1.96 for my $word (grep {length $_} split /[\x09-\x0D\x20]+/, $attr->value) {
345 wakaba 1.1 unless ($word{$word}) {
346     $word{$word} = 1;
347 wakaba 1.18 } elsif ($word eq 'up') {
348     #
349 wakaba 1.1 } else {
350 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'duplicate token',
351     value => $word,
352     level => $self->{level}->{must});
353 wakaba 1.1 }
354     }
355     ## NOTE: Case sensitive match (since HTML5 spec does not say link
356     ## types are case-insensitive and it says "The value should not
357     ## be confusingly similar to any other defined value (e.g.
358     ## differing only in case).").
359     ## NOTE: Though there is no explicit "MUST NOT" for undefined values,
360     ## "MAY"s and "only ... MAY" restrict non-standard non-registered
361     ## values to be used conformingly.
362 wakaba 1.66
363     my $is_hyperlink;
364     my $is_resource;
365 wakaba 1.1 require Whatpm::_LinkTypeList;
366     our $LinkType;
367     for my $word (keys %word) {
368     my $def = $LinkType->{$word};
369     if (defined $def) {
370     if ($def->{status} eq 'accepted') {
371     if (defined $def->{effect}->[$a_or_area]) {
372     #
373     } else {
374     $self->{onerror}->(node => $attr,
375 wakaba 1.104 type => 'link type:bad context',
376     value => $word,
377 wakaba 1.110 level => $self->{level}->{must});
378 wakaba 1.1 }
379     } elsif ($def->{status} eq 'proposal') {
380 wakaba 1.104 $self->{onerror}->(node => $attr,
381     type => 'link type:proposed',
382     value => $word,
383     level => $self->{level}->{should});
384 wakaba 1.20 if (defined $def->{effect}->[$a_or_area]) {
385     #
386     } else {
387     $self->{onerror}->(node => $attr,
388 wakaba 1.104 type => 'link type:bad context',
389     value => $word,
390     level => $self->{level}->{must});
391 wakaba 1.20 }
392 wakaba 1.1 } else { # rejected or synonym
393     $self->{onerror}->(node => $attr,
394 wakaba 1.104 type => 'link type:non-conforming',
395     value => $word,
396     level => $self->{level}->{must});
397 wakaba 1.1 }
398 wakaba 1.4 if (defined $def->{effect}->[$a_or_area]) {
399     if ($word eq 'alternate') {
400     #
401     } elsif ($def->{effect}->[$a_or_area] eq 'hyperlink') {
402 wakaba 1.66 $is_hyperlink = 1;
403 wakaba 1.4 }
404     }
405 wakaba 1.1 if ($def->{unique}) {
406     unless ($self->{has_link_type}->{$word}) {
407     $self->{has_link_type}->{$word} = 1;
408     } else {
409     $self->{onerror}->(node => $attr,
410 wakaba 1.104 type => 'link type:duplicate',
411     value => $word,
412     level => $self->{level}->{must});
413 wakaba 1.1 }
414     }
415 wakaba 1.66
416     if (defined $def->{effect}->[$a_or_area] and $word ne 'alternate') {
417     $is_hyperlink = 1 if $def->{effect}->[$a_or_area] eq 'hyperlink';
418     $is_resource = 1 if $def->{effect}->[$a_or_area] eq 'external resource';
419     }
420 wakaba 1.1 } else {
421 wakaba 1.104 $self->{onerror}->(node => $attr,
422     type => 'unknown link type',
423     value => $word,
424     level => $self->{level}->{uncertain});
425 wakaba 1.1 }
426     }
427 wakaba 1.66 $is_hyperlink = 1 if $word{alternate} and not $word{stylesheet};
428 wakaba 1.1 ## TODO: The Pingback 1.0 specification, which is referenced by HTML5,
429     ## says that using both X-Pingback: header field and HTML
430     ## <link rel=pingback> is deprecated and if both appears they
431     ## SHOULD contain exactly the same value.
432     ## ISSUE: Pingback 1.0 specification defines the exact representation
433     ## of its link element, which cannot be tested by the current arch.
434     ## ISSUE: Pingback 1.0 specification says that the document MUST NOT
435     ## include any string that matches to the pattern for the rel=pingback link,
436     ## which again inpossible to test.
437     ## ISSUE: rel=pingback href MUST NOT include entities other than predefined 4.
438 wakaba 1.12
439     ## NOTE: <link rel="up index"><link rel="up up index"> is not an error.
440 wakaba 1.17 ## NOTE: We can't check "If the page is part of multiple hierarchies,
441     ## then they SHOULD be described in different paragraphs.".
442 wakaba 1.66
443     $todo->{has_hyperlink_link_type} = 1 if $is_hyperlink;
444     if ($is_hyperlink or $a_or_area) {
445     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
446     }
447     if ($is_resource and not $a_or_area) {
448     $element_state->{uri_info}->{href}->{type}->{resource} = 1;
449     }
450 wakaba 1.96
451     $element_state->{link_rel} = \%word;
452 wakaba 1.1 }; # $HTMLLinkTypesAttrChecker
453 wakaba 1.20
454     ## 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."
455 wakaba 1.1
456     ## URI (or IRI)
457     my $HTMLURIAttrChecker = sub {
458 wakaba 1.66 my ($self, $attr, $item, $element_state) = @_;
459 wakaba 1.1 ## ISSUE: Relative references are allowed? (RFC 3987 "IRI" is an absolute reference with optional fragment identifier.)
460     my $value = $attr->value;
461     Whatpm::URIChecker->check_iri_reference ($value, sub {
462 wakaba 1.104 $self->{onerror}->(@_, node => $attr);
463 wakaba 1.106 }), $self->{level};
464 wakaba 1.17 $self->{has_uri_attr} = 1; ## TODO: <html manifest>
465 wakaba 1.66
466     my $attr_name = $attr->name;
467     $element_state->{uri_info}->{$attr_name}->{node} = $attr;
468     ## TODO: absolute
469     push @{$self->{return}->{uri}->{$value} ||= []},
470     $element_state->{uri_info}->{$attr_name};
471 wakaba 1.1 }; # $HTMLURIAttrChecker
472    
473     ## A space separated list of one or more URIs (or IRIs)
474     my $HTMLSpaceURIsAttrChecker = sub {
475     my ($self, $attr) = @_;
476 wakaba 1.66
477     my $type = {ping => 'action',
478     profile => 'namespace',
479     archive => 'resource'}->{$attr->name};
480    
481 wakaba 1.1 my $i = 0;
482     for my $value (split /[\x09-\x0D\x20]+/, $attr->value) {
483     Whatpm::URIChecker->check_iri_reference ($value, sub {
484 wakaba 1.104 $self->{onerror}->(value => $value, @_, node => $attr, index => $i);
485 wakaba 1.106 }, $self->{level});
486 wakaba 1.66
487     ## TODO: absolute
488     push @{$self->{return}->{uri}->{$value} ||= []},
489 wakaba 1.67 {node => $attr, type => {$type => 1}};
490 wakaba 1.66
491 wakaba 1.1 $i++;
492     }
493 wakaba 1.67 ## ISSUE: Relative references? (especially, in profile="")
494 wakaba 1.1 ## ISSUE: Leading or trailing white spaces are conformant?
495     ## ISSUE: A sequence of white space characters are conformant?
496     ## ISSUE: A zero-length string is conformant? (It does contain a relative reference, i.e. same as base URI.)
497     ## NOTE: Duplication seems not an error.
498 wakaba 1.4 $self->{has_uri_attr} = 1;
499 wakaba 1.1 }; # $HTMLSpaceURIsAttrChecker
500    
501     my $HTMLDatetimeAttrChecker = sub {
502     my ($self, $attr) = @_;
503     my $value = $attr->value;
504     ## ISSUE: "space", not "space character" (in parsing algorihtm, "space character")
505     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/) {
506     my ($y, $M, $d, $h, $m, $s, $f, $zh, $zm)
507     = ($1, $2, $3, $4, $5, $6, $7, $8, $9);
508     if (0 < $M and $M < 13) { ## ISSUE: This is not explicitly specified (though in parsing algorithm)
509 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'datetime:bad day',
510     level => $self->{level}->{must})
511 wakaba 1.1 if $d < 1 or
512     $d > [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]->[$M];
513 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'datetime:bad day',
514     level => $self->{level}->{must})
515 wakaba 1.1 if $M == 2 and $d == 29 and
516     not ($y % 400 == 0 or ($y % 4 == 0 and $y % 100 != 0));
517     } else {
518 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'datetime:bad month',
519     level => $self->{level}->{must});
520 wakaba 1.1 }
521 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'datetime:bad hour',
522     level => $self->{level}->{must}) if $h > 23;
523     $self->{onerror}->(node => $attr, type => 'datetime:bad minute',
524     level => $self->{level}->{must}) if $m > 59;
525     $self->{onerror}->(node => $attr, type => 'datetime:bad second',
526     level => $self->{level}->{must})
527 wakaba 1.1 if defined $s and $s > 59;
528 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'datetime:bad timezone hour',
529     level => $self->{level}->{must}) if $zh > 23;
530     $self->{onerror}->(node => $attr, type => 'datetime:bad timezone minute',
531     level => $self->{level}->{must}) if $zm > 59;
532 wakaba 1.1 ## ISSUE: Maybe timezone -00:00 should have same semantics as in RFC 3339.
533     } else {
534 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'datetime:syntax error',
535     level => $self->{level}->{must});
536 wakaba 1.1 }
537     }; # $HTMLDatetimeAttrChecker
538    
539     my $HTMLIntegerAttrChecker = sub {
540     my ($self, $attr) = @_;
541     my $value = $attr->value;
542     unless ($value =~ /\A-?[0-9]+\z/) {
543 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'integer:syntax error',
544     level => $self->{level}->{must});
545 wakaba 1.1 }
546     }; # $HTMLIntegerAttrChecker
547    
548     my $GetHTMLNonNegativeIntegerAttrChecker = sub {
549     my $range_check = shift;
550     return sub {
551     my ($self, $attr) = @_;
552     my $value = $attr->value;
553     if ($value =~ /\A[0-9]+\z/) {
554     unless ($range_check->($value + 0)) {
555 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'nninteger:out of range',
556     level => $self->{level}->{must});
557 wakaba 1.1 }
558     } else {
559     $self->{onerror}->(node => $attr,
560 wakaba 1.104 type => 'nninteger:syntax error',
561     level => $self->{level}->{must});
562 wakaba 1.1 }
563     };
564     }; # $GetHTMLNonNegativeIntegerAttrChecker
565    
566     my $GetHTMLFloatingPointNumberAttrChecker = sub {
567     my $range_check = shift;
568     return sub {
569     my ($self, $attr) = @_;
570     my $value = $attr->value;
571 wakaba 1.90 if ($value =~ /\A-?[0-9]+(?>\.[0-9]*)?\z/ or
572     $value =~ /\A-?\.[0-9]+\z/) {
573 wakaba 1.1 unless ($range_check->($value + 0)) {
574 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'float:out of range',
575     level => $self->{level}->{must});
576 wakaba 1.1 }
577     } else {
578     $self->{onerror}->(node => $attr,
579 wakaba 1.104 type => 'float:syntax error',
580     level => $self->{level}->{must});
581 wakaba 1.1 }
582     };
583     }; # $GetHTMLFloatingPointNumberAttrChecker
584    
585 wakaba 1.86 ## HTML4 %Length;
586     my $HTMLLengthAttrChecker = sub {
587     my ($self, $attr) = @_;
588     my $value = $attr->value;
589     unless ($value =~ /\A[0-9]+%?\z/) {
590     $self->{onerror}->(node => $attr, type => 'length:syntax error',
591 wakaba 1.104 level => $self->{level}->{must});
592 wakaba 1.86 }
593    
594     ## NOTE: HTML4 definition is too vague - it does not define the syntax
595     ## of percentage value at all (!).
596     }; # $HTMLLengthAttrChecker
597    
598 wakaba 1.1 ## "A valid MIME type, optionally with parameters. [RFC 2046]"
599     ## ISSUE: RFC 2046 does not define syntax of media types.
600     ## ISSUE: The definition of "a valid MIME type" is unknown.
601     ## Syntactical correctness?
602     my $HTMLIMTAttrChecker = sub {
603     my ($self, $attr) = @_;
604     my $value = $attr->value;
605     ## ISSUE: RFC 2045 Content-Type header field allows insertion
606     ## of LWS/comments between tokens. Is it allowed in HTML? Maybe no.
607     ## ISSUE: RFC 2231 extension? Maybe no.
608     my $lws0 = qr/(?>(?>\x0D\x0A)?[\x09\x20])*/;
609     my $token = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+/;
610     my $qs = qr/"(?>[\x00-\x0C\x0E-\x21\x23-\x5B\x5D-\x7E]|\x0D\x0A[\x09\x20]|\x5C[\x00-\x7F])*"/;
611     if ($value =~ m#\A$lws0($token)$lws0/$lws0($token)$lws0((?>;$lws0$token$lws0=$lws0(?>$token|$qs)$lws0)*)\z#) {
612     my @type = ($1, $2);
613     my $param = $3;
614     while ($param =~ s/^;$lws0($token)$lws0=$lws0(?>($token)|($qs))$lws0//) {
615     if (defined $2) {
616     push @type, $1 => $2;
617     } else {
618     my $n = $1;
619     my $v = $2;
620     $v =~ s/\\(.)/$1/gs;
621     push @type, $n => $v;
622     }
623     }
624     require Whatpm::IMTChecker;
625 wakaba 1.109 my $ic = Whatpm::IMTChecker->new;
626     $ic->{level} = $self->{level};
627     $ic->check_imt (sub {
628 wakaba 1.104 $self->{onerror}->(@_, node => $attr);
629 wakaba 1.1 }, @type);
630     } else {
631 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'IMT:syntax error',
632     level => $self->{level}->{must});
633 wakaba 1.1 }
634     }; # $HTMLIMTAttrChecker
635    
636     my $HTMLLanguageTagAttrChecker = sub {
637 wakaba 1.7 ## NOTE: See also $AtomLanguageTagAttrChecker in Atom.pm.
638    
639 wakaba 1.1 my ($self, $attr) = @_;
640 wakaba 1.6 my $value = $attr->value;
641     require Whatpm::LangTag;
642     Whatpm::LangTag->check_rfc3066_language_tag ($value, sub {
643 wakaba 1.104 $self->{onerror}->(@_, node => $attr);
644 wakaba 1.106 }, $self->{level});
645 wakaba 1.1 ## ISSUE: RFC 4646 (3066bis)?
646 wakaba 1.6
647     ## TODO: testdata
648 wakaba 1.1 }; # $HTMLLanguageTagAttrChecker
649    
650     ## "A valid media query [MQ]"
651     my $HTMLMQAttrChecker = sub {
652     my ($self, $attr) = @_;
653 wakaba 1.104 $self->{onerror}->(node => $attr,
654     type => 'media query',
655     level => $self->{level}->{uncertain});
656 wakaba 1.1 ## ISSUE: What is "a valid media query"?
657     }; # $HTMLMQAttrChecker
658    
659     my $HTMLEventHandlerAttrChecker = sub {
660     my ($self, $attr) = @_;
661 wakaba 1.104 $self->{onerror}->(node => $attr,
662     type => 'event handler',
663     level => $self->{level}->{uncertain});
664 wakaba 1.1 ## TODO: MUST contain valid ECMAScript code matching the
665     ## ECMAScript |FunctionBody| production. [ECMA262]
666     ## ISSUE: MUST be ES3? E4X? ES4? JS1.x?
667     ## ISSUE: Automatic semicolon insertion does not apply?
668     ## ISSUE: Other script languages?
669     }; # $HTMLEventHandlerAttrChecker
670    
671     my $HTMLUsemapAttrChecker = sub {
672     my ($self, $attr) = @_;
673 wakaba 1.100 ## MUST be a valid hash-name reference to a |map| element.
674 wakaba 1.1 my $value = $attr->value;
675     if ($value =~ s/^#//) {
676 wakaba 1.100 ## NOTE: |usemap="#"| is conforming, though it identifies no |map| element
677     ## according to the "rules for parsing a hash-name reference" algorithm.
678     ## The document is non-conforming anyway, since |<map name="">| (empty
679     ## name) is non-conforming.
680 wakaba 1.1 push @{$self->{usemap}}, [$value => $attr];
681     } else {
682 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'hashref:syntax error',
683     level => $self->{level}->{must});
684 wakaba 1.1 }
685 wakaba 1.100 ## NOTE: Space characters in hash-name references are conforming.
686 wakaba 1.1 ## ISSUE: UA algorithm for matching is case-insensitive; IDs only different in cases should be reported
687     }; # $HTMLUsemapAttrChecker
688    
689 wakaba 1.76 ## Valid browsing context name
690     my $HTMLBrowsingContextNameAttrChecker = sub {
691     my ($self, $attr) = @_;
692     my $value = $attr->value;
693     if ($value =~ /^_/) {
694     $self->{onerror}->(node => $attr, type => 'window name:reserved',
695 wakaba 1.104 level => $self->{level}->{must},
696 wakaba 1.76 value => $value);
697     } elsif (length $value) {
698     #
699     } else {
700     $self->{onerror}->(node => $attr, type => 'window name:empty',
701 wakaba 1.104 level => $self->{level}->{must});
702 wakaba 1.76 }
703     }; # $HTMLBrowsingContextNameAttrChecker
704    
705     ## Valid browsing context name or keyword
706 wakaba 1.1 my $HTMLTargetAttrChecker = sub {
707     my ($self, $attr) = @_;
708     my $value = $attr->value;
709     if ($value =~ /^_/) {
710     $value = lc $value; ## ISSUE: ASCII case-insentitive?
711     unless ({
712 wakaba 1.76 _blank => 1,_self => 1, _parent => 1, _top => 1,
713 wakaba 1.1 }->{$value}) {
714     $self->{onerror}->(node => $attr,
715 wakaba 1.76 type => 'window name:reserved',
716 wakaba 1.104 level => $self->{level}->{must},
717 wakaba 1.76 value => $value);
718 wakaba 1.1 }
719 wakaba 1.76 } elsif (length $value) {
720     #
721 wakaba 1.1 } else {
722 wakaba 1.76 $self->{onerror}->(node => $attr, type => 'window name:empty',
723 wakaba 1.104 level => $self->{level}->{must});
724 wakaba 1.1 }
725     }; # $HTMLTargetAttrChecker
726    
727 wakaba 1.23 my $HTMLSelectorsAttrChecker = sub {
728     my ($self, $attr) = @_;
729    
730     ## ISSUE: Namespace resolution?
731    
732     my $value = $attr->value;
733    
734     require Whatpm::CSS::SelectorsParser;
735     my $p = Whatpm::CSS::SelectorsParser->new;
736     $p->{pseudo_class}->{$_} = 1 for qw/
737     active checked disabled empty enabled first-child first-of-type
738     focus hover indeterminate last-child last-of-type link only-child
739     only-of-type root target visited
740     lang nth-child nth-last-child nth-of-type nth-last-of-type not
741     -manakai-contains -manakai-current
742     /;
743    
744     $p->{pseudo_element}->{$_} = 1 for qw/
745     after before first-letter first-line
746     /;
747    
748 wakaba 1.104 $p->{level} = $self->{level};
749 wakaba 1.23 $p->{onerror} = sub {
750 wakaba 1.104 $self->{onerror}->(@_, node => $attr);
751 wakaba 1.23 };
752     $p->parse_string ($value);
753     }; # $HTMLSelectorsAttrChecker
754    
755 wakaba 1.66 my $HTMLAccesskeyAttrChecker = sub {
756     my ($self, $attr) = @_;
757    
758     ## NOTE: "character" or |%Character;| in HTML4.
759    
760     my $value = $attr->value;
761     if (length $value != 1) {
762     $self->{onerror}->(node => $attr, type => 'char:syntax error',
763 wakaba 1.105 level => $self->{level}->{html4_fact});
764 wakaba 1.66 }
765    
766     ## NOTE: "Note. Authors should consider the input method of the expected
767     ## reader when specifying an accesskey." [HTML4] This is hard to implement,
768     ## since it depends on keyboard and so on.
769     ## NOTE: "We recommend that authors include the access key in label text
770     ## or wherever the access key is to apply." [HTML4] (informative)
771     }; # $HTMLAccesskeyAttrChecker
772    
773 wakaba 1.91 my $HTMLCharsetChecker = sub {
774     my ($charset_value, $self, $attr) = @_;
775    
776     ## NOTE: Though the case-sensitivility of |charset| attribute value
777     ## is not explicitly spelled in the HTML5 spec, the Character Set
778     ## registry of IANA, which is referenced from HTML5 spec, says that
779     ## charset name is case-insensitive.
780     $charset_value =~ tr/A-Z/a-z/; ## NOTE: ASCII Case-insensitive.
781    
782     require Message::Charset::Info;
783     my $charset = $Message::Charset::Info::IANACharset->{$charset_value};
784    
785     ## ISSUE: What is "valid character encoding name"? Syntactically valid?
786     ## Syntactically valid and registered? What about x-charset names?
787     unless (Message::Charset::Info::is_syntactically_valid_iana_charset_name
788     ($charset_value)) {
789     $self->{onerror}->(node => $attr,
790 wakaba 1.104 type => 'charset:syntax error',
791     value => $charset_value,
792     level => $self->{level}->{must});
793 wakaba 1.91 }
794    
795     if ($charset) {
796     ## ISSUE: What is "the preferred name for that encoding" (for a charset
797     ## with no "preferred MIME name" label)?
798     my $charset_status = $charset->{iana_names}->{$charset_value} || 0;
799     if (($charset_status &
800     Message::Charset::Info::PREFERRED_CHARSET_NAME ())
801     != Message::Charset::Info::PREFERRED_CHARSET_NAME ()) {
802     $self->{onerror}->(node => $attr,
803 wakaba 1.104 type => 'charset:not preferred',
804     value => $charset_value,
805     level => $self->{level}->{must});
806 wakaba 1.91 }
807     if (($charset_status &
808     Message::Charset::Info::REGISTERED_CHARSET_NAME ())
809     != Message::Charset::Info::REGISTERED_CHARSET_NAME ()) {
810     if ($charset_value =~ /^x-/) {
811     $self->{onerror}->(node => $attr,
812 wakaba 1.104 type => 'charset:private',
813     value => $charset_value,
814     level => $self->{level}->{good});
815 wakaba 1.91 } else {
816     $self->{onerror}->(node => $attr,
817 wakaba 1.104 type => 'charset:not registered',
818     value => $charset_value,
819     level => $self->{level}->{good});
820 wakaba 1.91 }
821     }
822     ## TODO: non-preferred-name error for following cases:
823     } elsif ($charset_value =~ /^x-/) {
824     $self->{onerror}->(node => $attr,
825 wakaba 1.104 type => 'charset:private',
826     value => $charset_value,
827     level => $self->{level}->{good});
828 wakaba 1.91 } else {
829     $self->{onerror}->(node => $attr,
830 wakaba 1.104 type => 'charset:not registered',
831     value => $charset_value,
832     level => $self->{level}->{good});
833 wakaba 1.91 }
834    
835     return ($charset, $charset_value);
836     }; # $HTMLCharsetChecker
837    
838 wakaba 1.68 my $HTMLColorAttrChecker = sub {
839     my ($self, $attr) = @_;
840    
841     ## NOTE: HTML4 "color" or |%Color;|
842    
843     my $value = $attr->value;
844    
845     if ($value !~ /\A(?>#[0-9A-F]+|black|silver|gray|white|maroon|red|purple|fuchsia|green|lime|olive|yellow|navy|blue|teal|aqua)\z/i) {
846 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'color:syntax error',
847 wakaba 1.105 level => $self->{level}->{html4_fact});
848 wakaba 1.68 }
849    
850     ## TODO: HTML4 has some guideline on usage of color.
851     }; # $HTMLColorAttrChecker
852    
853 wakaba 1.79 my $HTMLRefOrTemplateAttrChecker = sub {
854     my ($self, $attr) = @_;
855     $HTMLURIAttrChecker->(@_);
856    
857     my $attr_name = $attr->name;
858    
859     if ($attr_name eq 'ref') {
860     unless ($attr->owner_element->has_attribute_ns (undef, 'template')) {
861     $self->{onerror}->(node => $attr, type => 'attribute not allowed',
862 wakaba 1.104 level => $self->{level}->{must});
863 wakaba 1.79 }
864     }
865    
866     my $doc = $attr->owner_document;
867     my $doc_uri = $doc->document_uri;
868     my $uri = $doc->implementation
869     ->create_uri_reference ($attr->value)
870     ->get_absolute_reference ($doc_uri);
871     my $no_frag_uri = $uri->clone;
872     $no_frag_uri->uri_fragment (undef);
873     if ((defined $doc_uri and $doc_uri eq $no_frag_uri) or
874     (not defined $doc_uri and $no_frag_uri eq '')) {
875     my $fragid = $uri->uri_fragment;
876     if (defined $fragid) {
877     push @{$self->{$attr_name}}, [$fragid => $attr];
878     } else {
879     DOCEL: {
880     last DOCEL unless $attr_name eq 'template';
881    
882     my $docel = $doc->document_element;
883     if ($docel) {
884     my $nsuri = $docel->namespace_uri;
885     if (defined $nsuri and $nsuri eq $HTML_NS) {
886     if ($docel->manakai_local_name eq 'datatemplate') {
887     last DOCEL;
888     }
889     }
890     }
891    
892     $self->{onerror}->(node => $attr, type => 'template:not template',
893 wakaba 1.104 level => $self->{level}->{must});
894 wakaba 1.79 } # DOCEL
895     }
896     } else {
897     ## TODO: An external document is referenced.
898     ## The document MUST be an HTML or XML document.
899     ## If there is a fragment identifier, it MUST point a part of the doc.
900     ## If the attribute is |template|, the pointed part MUST be a
901     ## |datatemplat| element.
902     ## If no fragment identifier is specified, the root element MUST be
903     ## a |datatemplate| element when the attribute is |template|.
904     }
905     }; # $HTMLRefOrTemplateAttrChecker
906    
907 wakaba 1.83 my $HTMLRepeatIndexAttrChecker = sub {
908     my ($self, $attr) = @_;
909    
910     if (defined $attr->namespace_uri) {
911     my $oe = $attr->owner_element;
912     my $oe_nsuri = $oe->namespace_uri;
913     if (defined $oe_nsuri or $oe_nsuri eq $HTML_NS) {
914     $self->{onerror}->(node => $attr, type => 'attribute not allowed',
915 wakaba 1.104 level => $self->{level}->{must});
916 wakaba 1.83 }
917     }
918    
919     $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 })->(@_);
920     }; # $HTMLRepeatIndexAttrChecker
921    
922 wakaba 1.1 my $HTMLAttrChecker = {
923 wakaba 1.58 ## TODO: aria-* ## TODO: svg:*/@aria-* [HTML5ROLE] -> [STATES]
924 wakaba 1.1 id => sub {
925     ## NOTE: |map| has its own variant of |id=""| checker
926     my ($self, $attr) = @_;
927     my $value = $attr->value;
928     if (length $value > 0) {
929     if ($self->{id}->{$value}) {
930 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'duplicate ID',
931     level => $self->{level}->{must});
932 wakaba 1.1 push @{$self->{id}->{$value}}, $attr;
933     } else {
934     $self->{id}->{$value} = [$attr];
935     }
936     if ($value =~ /[\x09-\x0D\x20]/) {
937 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'space in ID',
938     level => $self->{level}->{must});
939 wakaba 1.1 }
940     } else {
941     ## NOTE: MUST contain at least one character
942 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'empty attribute value',
943     level => $self->{level}->{must});
944 wakaba 1.1 }
945     },
946     title => sub {}, ## NOTE: No conformance creteria
947     lang => sub {
948     my ($self, $attr) = @_;
949 wakaba 1.6 my $value = $attr->value;
950     if ($value eq '') {
951     #
952     } else {
953     require Whatpm::LangTag;
954     Whatpm::LangTag->check_rfc3066_language_tag ($value, sub {
955 wakaba 1.104 $self->{onerror}->(@_, node => $attr);
956 wakaba 1.106 }, $self->{level});
957 wakaba 1.6 }
958 wakaba 1.1 ## ISSUE: RFC 4646 (3066bis)?
959 wakaba 1.6
960     ## TODO: test data
961 wakaba 1.111
962     ## NOTE: Inconsistency between |lang| and |xml:lang| attributes are
963     ## non-conforming. Such errors are detected by the checkers of
964     ## |{}xml:lang| and |{xml}:lang| attributes.
965 wakaba 1.1 },
966     dir => $GetHTMLEnumeratedAttrChecker->({ltr => 1, rtl => 1}),
967     class => sub {
968     my ($self, $attr) = @_;
969     my %word;
970 wakaba 1.96 for my $word (grep {length $_} split /[\x09-\x0D\x20]+/, $attr->value) {
971 wakaba 1.1 unless ($word{$word}) {
972     $word{$word} = 1;
973     push @{$self->{return}->{class}->{$word}||=[]}, $attr;
974     } else {
975 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'duplicate token',
976     value => $word,
977     level => $self->{level}->{must});
978 wakaba 1.1 }
979     }
980     },
981 wakaba 1.63 contenteditable => $GetHTMLEnumeratedAttrChecker->({
982     true => 1, false => 1, '' => 1,
983     }),
984 wakaba 1.1 contextmenu => sub {
985     my ($self, $attr) = @_;
986     my $value = $attr->value;
987     push @{$self->{contextmenu}}, [$value => $attr];
988     ## ISSUE: "The value must be the ID of a menu element in the DOM."
989     ## What is "in the DOM"? A menu Element node that is not part
990     ## of the Document tree is in the DOM? A menu Element node that
991     ## belong to another Document tree is in the DOM?
992     },
993 wakaba 1.115 hidden => $GetHTMLBooleanAttrChecker->('hidden'),
994 wakaba 1.60 irrelevant => $GetHTMLBooleanAttrChecker->('irrelevant'),
995 wakaba 1.79 ref => $HTMLRefOrTemplateAttrChecker,
996     registrationmark => sub {
997     my ($self, $attr, $item, $element_state) = @_;
998    
999     ## NOTE: Any value is conforming.
1000    
1001     if ($self->{flag}->{in_rule}) {
1002     my $el = $attr->owner_element;
1003     my $ln = $el->manakai_local_name;
1004     if ($ln eq 'nest' or
1005     ($ln eq 'rule' and not $element_state->{in_rule_original})) {
1006     my $nsuri = $el->namespace_uri;
1007     if (defined $nsuri and $nsuri eq $HTML_NS) {
1008     $self->{onerror}->(node => $attr, type => 'attribute not allowed',
1009 wakaba 1.104 level => $self->{level}->{must});
1010 wakaba 1.79 }
1011     }
1012     } else {
1013     $self->{onerror}->(node => $attr, type => 'attribute not allowed',
1014 wakaba 1.104 level => $self->{level}->{must});
1015 wakaba 1.79 }
1016     },
1017 wakaba 1.80 repeat => sub {
1018     my ($self, $attr) = @_;
1019 wakaba 1.83
1020     if (defined $attr->namespace_uri) {
1021     my $oe = $attr->owner_element;
1022     my $oe_nsuri = $oe->namespace_uri;
1023     if (defined $oe_nsuri or $oe_nsuri eq $HTML_NS) {
1024     $self->{onerror}->(node => $attr, type => 'attribute not allowed',
1025 wakaba 1.104 level => $self->{level}->{must});
1026 wakaba 1.83 }
1027     }
1028    
1029 wakaba 1.80 my $value = $attr->value;
1030     if ($value eq 'template') {
1031     #
1032     } elsif ($value =~ /\A-?[0-9]+\z/) {
1033     #
1034     } else {
1035     $self->{onerror}->(node => $attr, type => 'repeat:syntax error',
1036 wakaba 1.104 level => $self->{level}->{must});
1037 wakaba 1.80 }
1038    
1039     ## ISSUE: "Repetition templates may occur anywhere." Does that mean
1040     ## that the attribute MAY be specified to any element, or that the
1041     ## element with that attribute (i.e. a repetition template) can be
1042     ## inserted anywhere in a document tree?
1043     },
1044 wakaba 1.83 'repeat-min' => $HTMLRepeatIndexAttrChecker,
1045     'repeat-max' => $HTMLRepeatIndexAttrChecker,
1046     'repeat-start' => $HTMLRepeatIndexAttrChecker,
1047 wakaba 1.80 'repeat-template' => sub {
1048 wakaba 1.83 my ($self, $attr) = @_;
1049    
1050     if (defined $attr->namespace_uri) {
1051     my $oe = $attr->owner_element;
1052     my $oe_nsuri = $oe->namespace_uri;
1053     if (defined $oe_nsuri or $oe_nsuri eq $HTML_NS) {
1054     $self->{onerror}->(node => $attr, type => 'attribute not allowed',
1055 wakaba 1.104 level => $self->{level}->{must});
1056 wakaba 1.83 }
1057     }
1058    
1059 wakaba 1.80 ## ISSUE: This attribute has no conformance requirement.
1060     ## ISSUE: Repetition blocks MAY have this attribute. Then, is the
1061     ## attribute allowed on an element that is not a repetition block?
1062     },
1063     ## TODO: namespaced variant of repeat-* attributes
1064 wakaba 1.58 ## TODO: role [HTML5ROLE] ## TODO: global @role [XHTML1ROLE]
1065 wakaba 1.78 ## TODO: style [HTML5]
1066 wakaba 1.74 tabindex => $HTMLIntegerAttrChecker,
1067 wakaba 1.79 template => $HTMLRefOrTemplateAttrChecker,
1068 wakaba 1.111 'xml:lang' => sub {
1069     my ($self, $attr) = @_;
1070    
1071     if ($attr->owner_document->manakai_is_html) {
1072     $self->{onerror}->(type => 'in HTML:xml:lang',
1073     level => $self->{level}->{info},
1074     node => $attr);
1075     ## NOTE: This is not an error, but the attribute will be ignored.
1076     } else {
1077     $self->{onerror}->(type => 'in XML:xml:lang',
1078     level => $self->{level}->{html5_no_may},
1079     node => $attr);
1080     ## TODO: We need to add test for this error.
1081     }
1082    
1083     my $lang_attr = $attr->owner_element->get_attribute_node_ns
1084     (undef, 'lang');
1085     if ($lang_attr) {
1086     my $lang_attr_value = $lang_attr->value;
1087     $lang_attr_value =~ tr/A-Z/a-z/; ## ASCII case-insensitive
1088     my $value = $attr->value;
1089     $value =~ tr/A-Z/a-z/; ## ASCII case-insensitive
1090     if ($lang_attr_value ne $value) {
1091     $self->{onerror}->(type => 'xml:lang ne lang',
1092     level => $self->{level}->{must},
1093     node => $attr);
1094     }
1095     } else {
1096     $self->{onerror}->(type => 'xml:lang not allowed',
1097     level => $self->{level}->{must},
1098     node => $attr);
1099     ## TODO: We need to add test for <x {xml}:lang {}xml:lang>.
1100     }
1101     },
1102 wakaba 1.74 xmlns => sub {
1103     my ($self, $attr) = @_;
1104     my $value = $attr->value;
1105     unless ($value eq $HTML_NS) {
1106 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'invalid attribute value',
1107     level => $self->{level}->{must});
1108 wakaba 1.74 ## TODO: Should be new "bad namespace" error?
1109     }
1110     unless ($attr->owner_document->manakai_is_html) {
1111 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'in XML:xmlns',
1112     level => $self->{level}->{must});
1113 wakaba 1.74 ## TODO: Test
1114     }
1115    
1116     ## TODO: Should be resolved?
1117     push @{$self->{return}->{uri}->{$value} ||= []},
1118     {node => $attr, type => {namespace => 1}};
1119     },
1120 wakaba 1.1 };
1121    
1122 wakaba 1.79 ## ISSUE: Shouldn't the same-origin policy applied to the datatemplate feature?
1123    
1124 wakaba 1.49 my %HTMLAttrStatus = (
1125 wakaba 1.50 class => FEATURE_HTML5_DEFAULT,
1126     contenteditable => FEATURE_HTML5_DEFAULT,
1127     contextmenu => FEATURE_HTML5_WD,
1128     dir => FEATURE_HTML5_DEFAULT,
1129     draggable => FEATURE_HTML5_LC,
1130 wakaba 1.115 hidden => FEATURE_HTML5_DEFAULT,
1131 wakaba 1.50 id => FEATURE_HTML5_DEFAULT,
1132 wakaba 1.115 irrelevant => FEATURE_HTML5_DROPPED,
1133 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT,
1134     ref => FEATURE_HTML5_AT_RISK,
1135     registrationmark => FEATURE_HTML5_AT_RISK,
1136 wakaba 1.60 repeat => FEATURE_WF2,
1137     'repeat-max' => FEATURE_WF2,
1138     'repeat-min' => FEATURE_WF2,
1139     'repeat-start' => FEATURE_WF2,
1140     'repeat-template' => FEATURE_WF2,
1141 wakaba 1.58 role => FEATURE_HTML5_ROLE,
1142 wakaba 1.78 style => FEATURE_HTML5_DEFAULT,
1143 wakaba 1.50 tabindex => FEATURE_HTML5_DEFAULT,
1144     template => FEATURE_HTML5_AT_RISK,
1145     title => FEATURE_HTML5_DEFAULT,
1146 wakaba 1.74 xmlns => FEATURE_HTML5_DEFAULT,
1147 wakaba 1.49 );
1148    
1149     my %HTMLM12NCommonAttrStatus = (
1150 wakaba 1.61 about => FEATURE_RDFA_LC,
1151 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1152 wakaba 1.61 content => FEATURE_RDFA_LC,
1153     datatype => FEATURE_RDFA_LC,
1154 wakaba 1.50 dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1155 wakaba 1.99 href => FEATURE_RDFA_LC,
1156 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1157 wakaba 1.61 instanceof => FEATURE_RDFA_LC,
1158 wakaba 1.50 onclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1159     ondblclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1160     onmousedown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1161     onmouseup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1162     onmouseover => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1163     onmousemove => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1164     onmouseout => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1165     onkeypress => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1166     onkeydown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1167     onkeyup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1168 wakaba 1.61 property => FEATURE_RDFA_LC,
1169     rel => FEATURE_RDFA_LC,
1170     resource => FEATURE_RDFA_LC,
1171     rev => FEATURE_RDFA_LC,
1172 wakaba 1.78 #style => FEATURE_HTML5_DEFAULT | FEATURE_XHTMLBASIC11_CR_DEPRECATED |
1173     # FEATURE_M12N10_REC,
1174     style => FEATURE_HTML5_DEFAULT | FEATURE_XHTMLBASIC11_CR |
1175 wakaba 1.55 FEATURE_M12N10_REC,
1176 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1177 wakaba 1.49 );
1178    
1179 wakaba 1.82 my %XHTML2CommonAttrStatus = (
1180     ## Core
1181 wakaba 1.108 class => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED,
1182     id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED,
1183 wakaba 1.82 #xml:id
1184     layout => FEATURE_XHTML2_ED,
1185 wakaba 1.108 title => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED,
1186 wakaba 1.82
1187     ## Hypertext
1188     cite => FEATURE_XHTML2_ED,
1189     href => FEATURE_XHTML2_ED,
1190     hreflang => FEATURE_XHTML2_ED,
1191     hrefmedia => FEATURE_XHTML2_ED,
1192     hreftype => FEATURE_XHTML2_ED,
1193     nextfocus => FEATURE_XHTML2_ED,
1194     prevfocus => FEATURE_XHTML2_ED,
1195     target => FEATURE_XHTML2_ED,
1196     #xml:base
1197    
1198     ## I18N
1199     #xml:lang
1200    
1201     ## Bi-directional
1202 wakaba 1.108 dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED,
1203 wakaba 1.82
1204     ## Edit
1205     edit => FEATURE_XHTML2_ED,
1206     datetime => FEATURE_XHTML2_ED,
1207    
1208     ## Embedding
1209     encoding => FEATURE_XHTML2_ED,
1210     src => FEATURE_XHTML2_ED,
1211     srctype => FEATURE_XHTML2_ED,
1212    
1213     ## Image Map
1214     usemap => FEATURE_XHTML2_ED,
1215     ismap => FEATURE_XHTML2_ED,
1216     shape => FEATURE_XHTML2_ED,
1217     coords => FEATURE_XHTML2_ED,
1218    
1219     ## Media
1220     media => FEATURE_XHTML2_ED,
1221    
1222     ## Metadata
1223     about => FEATURE_XHTML2_ED,
1224     content => FEATURE_XHTML2_ED,
1225     datatype => FEATURE_XHTML2_ED,
1226     instanceof => FEATURE_XHTML2_ED,
1227     property => FEATURE_XHTML2_ED,
1228     rel => FEATURE_XHTML2_ED,
1229     resource => FEATURE_XHTML2_ED,
1230     rev => FEATURE_XHTML2_ED,
1231    
1232     ## Role
1233 wakaba 1.108 role => FEATURE_HTML5_ROLE | FEATURE_XHTML2_ED,
1234 wakaba 1.82
1235     ## Style
1236 wakaba 1.108 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED, # "strongly discouraged"
1237 wakaba 1.82 );
1238    
1239     my %HTMLM12NXHTML2CommonAttrStatus = (
1240     %HTMLM12NCommonAttrStatus,
1241     %XHTML2CommonAttrStatus,
1242    
1243     about => FEATURE_RDFA_LC | FEATURE_XHTML2_ED,
1244     class => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1245     content => FEATURE_RDFA_LC | FEATURE_XHTML2_ED,
1246     datatype => FEATURE_RDFA_LC | FEATURE_XHTML2_ED,
1247     dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1248 wakaba 1.99 href => FEATURE_RDFA_LC,
1249 wakaba 1.82 id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1250     instanceof => FEATURE_RDFA_LC | FEATURE_XHTML2_ED,
1251     property => FEATURE_RDFA_LC | FEATURE_XHTML2_ED,
1252     rel => FEATURE_RDFA_LC | FEATURE_XHTML2_ED,
1253     resource => FEATURE_RDFA_LC | FEATURE_XHTML2_ED,
1254     rev => FEATURE_RDFA_LC | FEATURE_XHTML2_ED,
1255     #style => FEATURE_HTML5_DEFAULT | FEATURE_XHTMLBASIC11_CR_DEPRECATED |
1256     # FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1257     style => FEATURE_HTML5_DEFAULT | FEATURE_XHTMLBASIC11_CR |
1258     FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1259     title => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1260     );
1261    
1262 wakaba 1.1 for (qw/
1263     onabort onbeforeunload onblur onchange onclick oncontextmenu
1264     ondblclick ondrag ondragend ondragenter ondragleave ondragover
1265     ondragstart ondrop onerror onfocus onkeydown onkeypress
1266     onkeyup onload onmessage onmousedown onmousemove onmouseout
1267     onmouseover onmouseup onmousewheel onresize onscroll onselect
1268 wakaba 1.77 onstorage onsubmit onunload
1269 wakaba 1.1 /) {
1270     $HTMLAttrChecker->{$_} = $HTMLEventHandlerAttrChecker;
1271 wakaba 1.50 $HTMLAttrStatus{$_} = FEATURE_HTML5_DEFAULT;
1272 wakaba 1.1 }
1273    
1274 wakaba 1.82 ## NOTE: Non-standard global attributes in the HTML namespace.
1275     $AttrChecker->{$HTML_NS}->{''} = sub {}; # no syntactical checks
1276     $AttrStatus->{$HTML_NS}->{''} = 0; # disallowed and not part of any standard
1277    
1278     $AttrStatus->{$HTML_NS}->{active} = FEATURE_HTML5_DROPPED;
1279     for (qw/repeat repeat-max repeat-min repeat-start repeat-template/) {
1280     $AttrChecker->{$HTML_NS}->{$_} = $HTMLAttrChecker->{$_};
1281     $AttrStatus->{$HTML_NS}->{$_} = FEATURE_WF2;
1282     }
1283    
1284     for (qw/about content datatype instanceof property rel resource rev/) {
1285     $AttrStatus->{$HTML_NS}->{$_} = FEATURE_RDFA_LC | FEATURE_XHTML2_ED;
1286     }
1287     $AttrStatus->{$HTML_NS}->{typeof} = FEATURE_RDFA_ED;
1288     $AttrStatus->{$HTML_NS}->{role} = FEATURE_ROLE_LC;
1289     for (qw/cite coords datetime edit encoding href hreflang hrefmedia hreftype
1290     ismap layout media nextfocus prevfocus shape src srctype style
1291     target usemap/) {
1292     $AttrStatus->{$HTML_NS}->{$_} = FEATURE_XHTML2_ED;
1293     }
1294     for (qw/class dir id title/) {
1295     $AttrStatus->{$HTML_NS}->{$_} = FEATURE_M12N11_LC | FEATURE_XHTML2_ED;
1296     }
1297     for (qw/onclick ondblclick onmousedown onmouseup onmouseover onmousemove
1298     onmouseout onkeypress onkeydown onkeyup/) {
1299     $AttrStatus->{$HTML_NS}->{$_} = FEATURE_M12N11_LC;
1300     }
1301    
1302 wakaba 1.73 my $HTMLDatasetAttrChecker = sub {
1303     ## NOTE: "Authors should ... when the attributes are ignored and
1304     ## any associated CSS dropped, the page is still usable." (semantic
1305     ## constraint.)
1306     }; # $HTMLDatasetAttrChecker
1307    
1308     my $HTMLDatasetAttrStatus = FEATURE_HTML5_DEFAULT;
1309    
1310 wakaba 1.1 my $GetHTMLAttrsChecker = sub {
1311     my $element_specific_checker = shift;
1312 wakaba 1.49 my $element_specific_status = shift;
1313 wakaba 1.1 return sub {
1314 wakaba 1.40 my ($self, $item, $element_state) = @_;
1315     for my $attr (@{$item->{node}->attributes}) {
1316 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
1317     $attr_ns = '' unless defined $attr_ns;
1318     my $attr_ln = $attr->manakai_local_name;
1319     my $checker;
1320 wakaba 1.73 my $status;
1321 wakaba 1.1 if ($attr_ns eq '') {
1322 wakaba 1.117 if ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/) {
1323 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
1324     $status = $HTMLDatasetAttrStatus;
1325     } else {
1326     $checker = $element_specific_checker->{$attr_ln}
1327     || $HTMLAttrChecker->{$attr_ln};
1328     $status = $element_specific_status->{$attr_ln};
1329     }
1330 wakaba 1.1 }
1331     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
1332 wakaba 1.40 || $AttrChecker->{$attr_ns}->{''};
1333 wakaba 1.82 $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
1334     || $AttrStatus->{$attr_ns}->{''};
1335     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
1336 wakaba 1.1 if ($checker) {
1337 wakaba 1.66 $checker->($self, $attr, $item, $element_state);
1338 wakaba 1.62 } elsif ($attr_ns eq '' and not $element_specific_status->{$attr_ln}) {
1339 wakaba 1.54 #
1340 wakaba 1.1 } else {
1341 wakaba 1.104 $self->{onerror}->(node => $attr,
1342     type => 'unknown attribute',
1343     level => $self->{level}->{uncertain});
1344 wakaba 1.49 ## ISSUE: No conformance createria for unknown attributes in the spec
1345     }
1346 wakaba 1.82 $self->_attr_status_info ($attr, $status);
1347 wakaba 1.1 }
1348     };
1349     }; # $GetHTMLAttrsChecker
1350    
1351 wakaba 1.40 my %HTMLChecker = (
1352     %Whatpm::ContentChecker::AnyChecker,
1353 wakaba 1.79 check_start => sub {
1354     my ($self, $item, $element_state) = @_;
1355    
1356     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
1357     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
1358     },
1359 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, \%HTMLAttrStatus),
1360 wakaba 1.40 );
1361    
1362     my %HTMLEmptyChecker = (
1363     %HTMLChecker,
1364     check_child_element => sub {
1365     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1366     $child_is_transparent, $element_state) = @_;
1367     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
1368     $self->{onerror}->(node => $child_el,
1369     type => 'element not allowed:minus',
1370 wakaba 1.104 level => $self->{level}->{must});
1371 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1372     #
1373     } else {
1374     $self->{onerror}->(node => $child_el,
1375     type => 'element not allowed:empty',
1376 wakaba 1.104 level => $self->{level}->{must});
1377 wakaba 1.40 }
1378     },
1379     check_child_text => sub {
1380     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1381     if ($has_significant) {
1382     $self->{onerror}->(node => $child_node,
1383     type => 'character not allowed:empty',
1384 wakaba 1.104 level => $self->{level}->{must});
1385 wakaba 1.40 }
1386     },
1387     );
1388    
1389     my %HTMLTextChecker = (
1390     %HTMLChecker,
1391     check_child_element => sub {
1392     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1393     $child_is_transparent, $element_state) = @_;
1394     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
1395     $self->{onerror}->(node => $child_el,
1396     type => 'element not allowed:minus',
1397 wakaba 1.104 level => $self->{level}->{must});
1398 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1399     #
1400     } else {
1401 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed:text',
1402     level => $self->{level}->{must});
1403 wakaba 1.40 }
1404     },
1405     );
1406    
1407 wakaba 1.72 my %HTMLFlowContentChecker = (
1408 wakaba 1.40 %HTMLChecker,
1409     check_child_element => sub {
1410     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1411     $child_is_transparent, $element_state) = @_;
1412     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
1413     $self->{onerror}->(node => $child_el,
1414     type => 'element not allowed:minus',
1415 wakaba 1.104 level => $self->{level}->{must});
1416 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1417     #
1418     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') {
1419     if ($element_state->{has_non_style} or
1420     not $child_el->has_attribute_ns (undef, 'scoped')) {
1421 wakaba 1.104 $self->{onerror}->(node => $child_el,
1422 wakaba 1.72 type => 'element not allowed:flow style',
1423 wakaba 1.104 level => $self->{level}->{must});
1424 wakaba 1.40 }
1425 wakaba 1.72 } elsif ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) {
1426 wakaba 1.43 $element_state->{has_non_style} = 1 unless $child_is_transparent;
1427 wakaba 1.40 } else {
1428     $element_state->{has_non_style} = 1;
1429 wakaba 1.104 $self->{onerror}->(node => $child_el,
1430 wakaba 1.72 type => 'element not allowed:flow',
1431 wakaba 1.104 level => $self->{level}->{must})
1432 wakaba 1.40 }
1433     },
1434     check_child_text => sub {
1435     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1436     if ($has_significant) {
1437     $element_state->{has_non_style} = 1;
1438     }
1439     },
1440     check_end => sub {
1441     my ($self, $item, $element_state) = @_;
1442 wakaba 1.95 ## NOTE: A modified copy of the code below is in |datagrid| checker.
1443 wakaba 1.40 if ($element_state->{has_significant}) {
1444 wakaba 1.46 $item->{real_parent_state}->{has_significant} = 1;
1445 wakaba 1.40 } elsif ($item->{transparent}) {
1446     #
1447     } else {
1448     $self->{onerror}->(node => $item->{node},
1449 wakaba 1.104 level => $self->{level}->{should},
1450 wakaba 1.40 type => 'no significant content');
1451     }
1452     },
1453     );
1454    
1455     my %HTMLPhrasingContentChecker = (
1456     %HTMLChecker,
1457     check_child_element => sub {
1458     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1459     $child_is_transparent, $element_state) = @_;
1460     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
1461     $self->{onerror}->(node => $child_el,
1462     type => 'element not allowed:minus',
1463 wakaba 1.104 level => $self->{level}->{must});
1464 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1465     #
1466     } elsif ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
1467     #
1468     } else {
1469     $self->{onerror}->(node => $child_el,
1470     type => 'element not allowed:phrasing',
1471 wakaba 1.104 level => $self->{level}->{must});
1472 wakaba 1.40 }
1473     },
1474 wakaba 1.72 check_end => $HTMLFlowContentChecker{check_end},
1475 wakaba 1.40 ## NOTE: The definition for |li| assumes that the only differences
1476 wakaba 1.72 ## between flow and phrasing content checkers are |check_child_element|
1477 wakaba 1.40 ## and |check_child_text|.
1478     );
1479    
1480 wakaba 1.72 my %HTMLTransparentChecker = %HTMLFlowContentChecker;
1481 wakaba 1.40 ## ISSUE: Significant content rule should be applied to transparent element
1482 wakaba 1.46 ## with parent?
1483 wakaba 1.40
1484 wakaba 1.1 our $Element;
1485     our $ElementDefault;
1486    
1487     $Element->{$HTML_NS}->{''} = {
1488 wakaba 1.40 %HTMLChecker,
1489 wakaba 1.1 };
1490    
1491     $Element->{$HTML_NS}->{html} = {
1492 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1493 wakaba 1.1 is_root => 1,
1494 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
1495 wakaba 1.16 manifest => $HTMLURIAttrChecker,
1496 wakaba 1.67 version => sub {
1497     ## NOTE: According to HTML4 prose, this is a "cdata" attribute.
1498     ## Though DTDs of various versions of HTML define the attribute
1499     ## as |#FIXED|, this conformance checker does no check for
1500     ## the attribute value, since what kind of check should be done
1501     ## is unknown.
1502     },
1503 wakaba 1.49 }, {
1504     %HTMLAttrStatus,
1505 wakaba 1.82 %XHTML2CommonAttrStatus,
1506     class => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_HTML2X_RFC,
1507     dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1508     id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
1509 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1510     manifest => FEATURE_HTML5_DEFAULT,
1511 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
1512 wakaba 1.82 version => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1513 wakaba 1.1 }),
1514 wakaba 1.40 check_start => sub {
1515     my ($self, $item, $element_state) = @_;
1516     $element_state->{phase} = 'before head';
1517 wakaba 1.79
1518 wakaba 1.66 $element_state->{uri_info}->{manifest}->{type}->{resource} = 1;
1519 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
1520     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
1521 wakaba 1.40 },
1522     check_child_element => sub {
1523     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1524     $child_is_transparent, $element_state) = @_;
1525     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
1526     $self->{onerror}->(node => $child_el,
1527     type => 'element not allowed:minus',
1528 wakaba 1.104 level => $self->{level}->{must});
1529 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1530     #
1531     } elsif ($element_state->{phase} eq 'before head') {
1532     if ($child_nsuri eq $HTML_NS and $child_ln eq 'head') {
1533     $element_state->{phase} = 'after head';
1534     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'body') {
1535     $self->{onerror}->(node => $child_el,
1536 wakaba 1.104 type => 'ps element missing',
1537     text => 'head',
1538     level => $self->{level}->{must});
1539 wakaba 1.40 $element_state->{phase} = 'after body';
1540     } else {
1541     $self->{onerror}->(node => $child_el,
1542 wakaba 1.104 type => 'element not allowed',
1543     level => $self->{level}->{must});
1544 wakaba 1.40 }
1545     } elsif ($element_state->{phase} eq 'after head') {
1546     if ($child_nsuri eq $HTML_NS and $child_ln eq 'body') {
1547     $element_state->{phase} = 'after body';
1548     } else {
1549     $self->{onerror}->(node => $child_el,
1550 wakaba 1.104 type => 'element not allowed',
1551     level => $self->{level}->{must});
1552 wakaba 1.40 }
1553     } elsif ($element_state->{phase} eq 'after body') {
1554     $self->{onerror}->(node => $child_el,
1555 wakaba 1.104 type => 'element not allowed',
1556     level => $self->{level}->{must});
1557 wakaba 1.40 } else {
1558     die "check_child_element: Bad |html| phase: $element_state->{phase}";
1559     }
1560     },
1561     check_child_text => sub {
1562     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1563     if ($has_significant) {
1564     $self->{onerror}->(node => $child_node,
1565 wakaba 1.104 type => 'character not allowed',
1566     level => $self->{level}->{must});
1567 wakaba 1.40 }
1568     },
1569     check_end => sub {
1570     my ($self, $item, $element_state) = @_;
1571     if ($element_state->{phase} eq 'after body') {
1572     #
1573     } elsif ($element_state->{phase} eq 'before head') {
1574     $self->{onerror}->(node => $item->{node},
1575 wakaba 1.104 type => 'child element missing',
1576     text => 'head',
1577     level => $self->{level}->{must});
1578 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1579 wakaba 1.104 type => 'child element missing',
1580     text => 'body',
1581     level => $self->{level}->{must});
1582 wakaba 1.40 } elsif ($element_state->{phase} eq 'after head') {
1583     $self->{onerror}->(node => $item->{node},
1584 wakaba 1.104 type => 'child element missing',
1585     text => 'body',
1586     level => $self->{level}->{must});
1587 wakaba 1.40 } else {
1588     die "check_end: Bad |html| phase: $element_state->{phase}";
1589     }
1590 wakaba 1.1
1591 wakaba 1.40 $HTMLChecker{check_end}->(@_);
1592     },
1593     };
1594 wakaba 1.25
1595 wakaba 1.40 $Element->{$HTML_NS}->{head} = {
1596 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1597 wakaba 1.67 check_attrs => $GetHTMLAttrsChecker->({
1598     profile => $HTMLSpaceURIsAttrChecker, ## NOTE: MUST be profile URIs.
1599     }, {
1600 wakaba 1.49 %HTMLAttrStatus,
1601 wakaba 1.82 %XHTML2CommonAttrStatus,
1602     class => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_HTML2X_RFC,
1603     dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1604     id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
1605 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1606 wakaba 1.49 profile => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC,
1607     }),
1608 wakaba 1.40 check_child_element => sub {
1609     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1610     $child_is_transparent, $element_state) = @_;
1611     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
1612     $self->{onerror}->(node => $child_el,
1613     type => 'element not allowed:minus',
1614 wakaba 1.104 level => $self->{level}->{must});
1615 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1616     #
1617     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'title') {
1618     unless ($element_state->{has_title}) {
1619     $element_state->{has_title} = 1;
1620     } else {
1621     $self->{onerror}->(node => $child_el,
1622     type => 'element not allowed:head title',
1623 wakaba 1.104 level => $self->{level}->{must});
1624 wakaba 1.40 }
1625     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') {
1626     if ($child_el->has_attribute_ns (undef, 'scoped')) {
1627     $self->{onerror}->(node => $child_el,
1628     type => 'element not allowed:head style',
1629 wakaba 1.104 level => $self->{level}->{must});
1630 wakaba 1.1 }
1631 wakaba 1.40 } elsif ($HTMLMetadataContent->{$child_nsuri}->{$child_ln}) {
1632     #
1633    
1634     ## NOTE: |meta| is a metadata content. However, strictly speaking,
1635     ## a |meta| element with none of |charset|, |name|,
1636     ## or |http-equiv| attribute is not allowed. It is non-conforming
1637     ## anyway.
1638 wakaba 1.56
1639     ## TODO: |form| MUST be empty and in XML [WF2].
1640 wakaba 1.40 } else {
1641     $self->{onerror}->(node => $child_el,
1642     type => 'element not allowed:metadata',
1643 wakaba 1.104 level => $self->{level}->{must});
1644 wakaba 1.40 }
1645     $element_state->{in_head_original} = $self->{flag}->{in_head};
1646     $self->{flag}->{in_head} = 1;
1647     },
1648     check_child_text => sub {
1649     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1650     if ($has_significant) {
1651 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
1652     level => $self->{level}->{must});
1653 wakaba 1.1 }
1654 wakaba 1.40 },
1655     check_end => sub {
1656     my ($self, $item, $element_state) = @_;
1657     unless ($element_state->{has_title}) {
1658     $self->{onerror}->(node => $item->{node},
1659 wakaba 1.104 type => 'child element missing',
1660     text => 'title',
1661 wakaba 1.105 level => $self->{level}->{must});
1662 wakaba 1.1 }
1663 wakaba 1.40 $self->{flag}->{in_head} = $element_state->{in_head_original};
1664 wakaba 1.1
1665 wakaba 1.40 $HTMLChecker{check_end}->(@_);
1666 wakaba 1.1 },
1667     };
1668    
1669 wakaba 1.40 $Element->{$HTML_NS}->{title} = {
1670     %HTMLTextChecker,
1671 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1672 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
1673     %HTMLAttrStatus,
1674 wakaba 1.82 %XHTML2CommonAttrStatus,
1675     class => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_HTML2X_RFC,
1676     dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1677     id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
1678 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1679 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
1680 wakaba 1.49 }),
1681 wakaba 1.40 };
1682 wakaba 1.1
1683 wakaba 1.40 $Element->{$HTML_NS}->{base} = {
1684 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1685 wakaba 1.40 %HTMLEmptyChecker,
1686     check_attrs => sub {
1687     my ($self, $item, $element_state) = @_;
1688 wakaba 1.1
1689 wakaba 1.40 if ($self->{has_base}) {
1690     $self->{onerror}->(node => $item->{node},
1691 wakaba 1.104 type => 'element not allowed:base',
1692     level => $self->{level}->{must});
1693 wakaba 1.40 } else {
1694     $self->{has_base} = 1;
1695 wakaba 1.29 }
1696    
1697 wakaba 1.40 my $has_href = $item->{node}->has_attribute_ns (undef, 'href');
1698     my $has_target = $item->{node}->has_attribute_ns (undef, 'target');
1699 wakaba 1.14
1700     if ($self->{has_uri_attr} and $has_href) {
1701 wakaba 1.4 ## ISSUE: Are these examples conforming?
1702     ## <head profile="a b c"><base href> (except for |profile|'s
1703     ## non-conformance)
1704     ## <title xml:base="relative"/><base href/> (maybe it should be)
1705     ## <unknown xmlns="relative"/><base href/> (assuming that
1706     ## |{relative}:unknown| is allowed before XHTML |base| (unlikely, though))
1707     ## <style>@import 'relative';</style><base href>
1708     ## <script>location.href = 'relative';</script><base href>
1709 wakaba 1.14 ## NOTE: <html manifest=".."><head><base href=""/> is conforming as
1710     ## an exception.
1711 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1712 wakaba 1.104 type => 'basehref after URL attribute',
1713     level => $self->{level}->{must});
1714 wakaba 1.4 }
1715 wakaba 1.14 if ($self->{has_hyperlink_element} and $has_target) {
1716 wakaba 1.4 ## ISSUE: Are these examples conforming?
1717     ## <head><title xlink:href=""/><base target="name"/></head>
1718     ## <xbl:xbl>...<svg:a href=""/>...</xbl:xbl><base target="name"/>
1719     ## (assuming that |xbl:xbl| is allowed before |base|)
1720     ## NOTE: These are non-conformant anyway because of |head|'s content model:
1721     ## <link href=""/><base target="name"/>
1722     ## <link rel=unknown href=""><base target=name>
1723 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1724 wakaba 1.104 type => 'basetarget after hyperlink',
1725     level => $self->{level}->{must});
1726 wakaba 1.4 }
1727    
1728 wakaba 1.14 if (not $has_href and not $has_target) {
1729 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1730 wakaba 1.104 type => 'attribute missing:href|target',
1731     level => $self->{level}->{must});
1732 wakaba 1.14 }
1733    
1734 wakaba 1.66 $element_state->{uri_info}->{href}->{type}->{base} = 1;
1735    
1736 wakaba 1.4 return $GetHTMLAttrsChecker->({
1737     href => $HTMLURIAttrChecker,
1738     target => $HTMLTargetAttrChecker,
1739 wakaba 1.49 }, {
1740     %HTMLAttrStatus,
1741 wakaba 1.50 href => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1742     id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1743     target => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1744 wakaba 1.40 })->($self, $item, $element_state);
1745 wakaba 1.4 },
1746 wakaba 1.1 };
1747    
1748     $Element->{$HTML_NS}->{link} = {
1749 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1750 wakaba 1.40 %HTMLEmptyChecker,
1751     check_attrs => sub {
1752     my ($self, $item, $element_state) = @_;
1753 wakaba 1.96 my $sizes_attr;
1754 wakaba 1.1 $GetHTMLAttrsChecker->({
1755 wakaba 1.91 charset => sub {
1756     my ($self, $attr) = @_;
1757     $HTMLCharsetChecker->($attr->value, @_);
1758     },
1759 wakaba 1.1 href => $HTMLURIAttrChecker,
1760 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(0, $item, @_) },
1761 wakaba 1.92 rev => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(),
1762 wakaba 1.1 media => $HTMLMQAttrChecker,
1763     hreflang => $HTMLLanguageTagAttrChecker,
1764 wakaba 1.96 sizes => sub {
1765     my ($self, $attr) = @_;
1766     $sizes_attr = $attr;
1767     my %word;
1768     for my $word (grep {length $_}
1769     split /[\x09-\x0D\x20]+/, $attr->value) {
1770     unless ($word{$word}) {
1771     $word{$word} = 1;
1772     if ($word eq 'any' or $word =~ /\A[1-9][0-9]*x[1-9][0-9]*\z/) {
1773     #
1774     } else {
1775     $self->{onerror}->(node => $attr,
1776 wakaba 1.104 type => 'sizes:syntax error',
1777 wakaba 1.96 value => $word,
1778 wakaba 1.104 level => $self->{level}->{must});
1779 wakaba 1.96 }
1780     } else {
1781     $self->{onerror}->(node => $attr, type => 'duplicate token',
1782     value => $word,
1783 wakaba 1.104 level => $self->{level}->{must});
1784 wakaba 1.96 }
1785     }
1786     },
1787 wakaba 1.70 target => $HTMLTargetAttrChecker,
1788 wakaba 1.1 type => $HTMLIMTAttrChecker,
1789     ## NOTE: Though |title| has special semantics,
1790     ## syntactically same as the |title| as global attribute.
1791 wakaba 1.49 }, {
1792     %HTMLAttrStatus,
1793 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
1794 wakaba 1.91 charset => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC,
1795     ## NOTE: |charset| attribute had been part of HTML5 spec though
1796     ## it had been commented out.
1797 wakaba 1.99 href => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1798 wakaba 1.82 hreflang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED |
1799     FEATURE_M12N10_REC,
1800 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1801 wakaba 1.82 media => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1802 wakaba 1.61 methods => FEATURE_HTML20_RFC,
1803 wakaba 1.99 rel => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1804     rev => FEATURE_RDFA_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1805 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
1806 wakaba 1.96 sizes => FEATURE_HTML5_DEFAULT,
1807 wakaba 1.82 target => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1808 wakaba 1.50 type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1809 wakaba 1.61 urn => FEATURE_HTML20_RFC,
1810 wakaba 1.40 })->($self, $item, $element_state);
1811 wakaba 1.96
1812 wakaba 1.40 if ($item->{node}->has_attribute_ns (undef, 'href')) {
1813     $self->{has_hyperlink_element} = 1 if $item->{has_hyperlink_link_type};
1814 wakaba 1.4 } else {
1815 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1816 wakaba 1.104 type => 'attribute missing',
1817     text => 'href',
1818     level => $self->{level}->{must});
1819 wakaba 1.1 }
1820 wakaba 1.96
1821 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'rel')) {
1822     $self->{onerror}->(node => $item->{node},
1823 wakaba 1.104 type => 'attribute missing',
1824     text => 'rel',
1825     level => $self->{level}->{must});
1826 wakaba 1.96 }
1827    
1828     if ($sizes_attr and not $element_state->{link_rel}->{icon}) {
1829     $self->{onerror}->(node => $sizes_attr,
1830     type => 'attribute not allowed',
1831 wakaba 1.104 level => $self->{level}->{must});
1832 wakaba 1.1 }
1833 wakaba 1.116
1834     if ($element_state->{link_rel}->{alternate} and
1835     $element_state->{link_rel}->{stylesheet}) {
1836     my $title_attr = $item->{node}->get_attribute_node_ns (undef, 'title');
1837     unless ($title_attr) {
1838     $self->{onerror}->(node => $item->{node},
1839     type => 'attribute missing',
1840     text => 'title',
1841     level => $self->{level}->{must});
1842     } elsif ($title_attr->value eq '') {
1843     $self->{onerror}->(node => $title_attr,
1844     type => 'empty style sheet title',
1845     level => $self->{level}->{must});
1846     }
1847     }
1848 wakaba 1.1 },
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.117 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/) {
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.117 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/) {
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.117 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/) {
4027 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
4028     $status = $HTMLDatasetAttrStatus;
4029 wakaba 1.117 } elsif ($attr_ln !~ /^[Xx][Mm][Ll]/ and
4030     $attr_ln =~ /\A\p{InXML_NCNameStartChar10}\p{InXMLNCNameChar10}*\z/) {
4031 wakaba 1.1 $checker = $HTMLAttrChecker->{$attr_ln}
4032     || sub { }; ## NOTE: Any local attribute is ok.
4033 wakaba 1.98 $status = FEATURE_HTML5_WD | FEATURE_ALLOWED;
4034 wakaba 1.117 } else {
4035     $checker = $HTMLAttrChecker->{$attr_ln};
4036 wakaba 1.1 }
4037     }
4038     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
4039 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
4040     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
4041     || $AttrStatus->{$attr_ns}->{''};
4042     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
4043 wakaba 1.62
4044 wakaba 1.1 if ($checker) {
4045 wakaba 1.66 $checker->($self, $attr, $item, $element_state);
4046 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
4047 wakaba 1.54 #
4048 wakaba 1.1 } else {
4049 wakaba 1.104 $self->{onerror}->(node => $attr,
4050     type => 'unknown attribute',
4051     level => $self->{level}->{uncertain});
4052 wakaba 1.50 ## ISSUE: No conformance createria for global attributes in the spec
4053     }
4054    
4055 wakaba 1.82 $self->_attr_status_info ($attr, $status);
4056 wakaba 1.1 }
4057    
4058     unless ($has_src) {
4059 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4060 wakaba 1.104 type => 'attribute missing',
4061     text => 'src',
4062 wakaba 1.114 level => $self->{level}->{info});
4063     ## NOTE: <embed> without src="" is allowed since revision 1929.
4064     ## We issues an informational message since <embed> w/o src=""
4065     ## is likely an authoring error.
4066 wakaba 1.1 }
4067 wakaba 1.114
4068     ## TODO: external resource check
4069 wakaba 1.66
4070     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4071 wakaba 1.1 },
4072     };
4073    
4074 wakaba 1.49 ## TODO:
4075     ## {applet} FEATURE_M12N10_REC_DEPRECATED
4076     ## class, id, title, alt, archive, code, codebase, height, object, width name style,hspace,vspace(xhtml10)
4077    
4078 wakaba 1.1 $Element->{$HTML_NS}->{object} = {
4079 wakaba 1.40 %HTMLTransparentChecker,
4080 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4081 wakaba 1.40 check_attrs => sub {
4082     my ($self, $item, $element_state) = @_;
4083 wakaba 1.1 $GetHTMLAttrsChecker->({
4084 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
4085     bottom => 1, middle => 1, top => 1, left => 1, right => 1,
4086     }),
4087     archive => $HTMLSpaceURIsAttrChecker,
4088     ## TODO: Relative to @codebase
4089     border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4090     classid => $HTMLURIAttrChecker,
4091     codebase => $HTMLURIAttrChecker,
4092     codetype => $HTMLIMTAttrChecker,
4093     ## TODO: "RECOMMENDED when |classid| is specified" [HTML4]
4094 wakaba 1.1 data => $HTMLURIAttrChecker,
4095 wakaba 1.70 declare => $GetHTMLBooleanAttrChecker->('declare'),
4096     ## NOTE: "The object MUST be instantiated by a subsequent OBJECT ..."
4097     ## [HTML4] but we don't know how to test this.
4098     hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4099 wakaba 1.76 name => $HTMLBrowsingContextNameAttrChecker,
4100 wakaba 1.70 standby => sub {}, ## NOTE: %Text; in HTML4
4101 wakaba 1.1 type => $HTMLIMTAttrChecker,
4102     usemap => $HTMLUsemapAttrChecker,
4103 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4104 wakaba 1.70 vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4105 wakaba 1.92 width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4106 wakaba 1.49 }, {
4107     %HTMLAttrStatus,
4108 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4109 wakaba 1.49 align => FEATURE_XHTML10_REC,
4110 wakaba 1.82 archive => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4111 wakaba 1.49 border => FEATURE_XHTML10_REC,
4112     classid => FEATURE_M12N10_REC,
4113     codebase => FEATURE_M12N10_REC,
4114     codetype => FEATURE_M12N10_REC,
4115 wakaba 1.82 'content-length' => FEATURE_XHTML2_ED,
4116 wakaba 1.50 data => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4117 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
4118     dataformatas => FEATURE_HTML4_REC_RESERVED,
4119     datasrc => FEATURE_HTML4_REC_RESERVED,
4120 wakaba 1.82 declare => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4121 wakaba 1.50 height => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4122 wakaba 1.49 hspace => FEATURE_XHTML10_REC,
4123 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4124 wakaba 1.76 name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4125 wakaba 1.49 standby => FEATURE_M12N10_REC,
4126 wakaba 1.50 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4127     type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4128     usemap => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4129 wakaba 1.49 vspace => FEATURE_XHTML10_REC,
4130 wakaba 1.50 width => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4131 wakaba 1.66 })->($self, $item, $element_state);
4132 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'data')) {
4133     unless ($item->{node}->has_attribute_ns (undef, 'type')) {
4134     $self->{onerror}->(node => $item->{node},
4135 wakaba 1.104 type => 'attribute missing:data|type',
4136     level => $self->{level}->{must});
4137 wakaba 1.1 }
4138     }
4139 wakaba 1.66
4140     $element_state->{uri_info}->{data}->{type}->{embedded} = 1;
4141     $element_state->{uri_info}->{classid}->{type}->{embedded} = 1;
4142     $element_state->{uri_info}->{codebase}->{type}->{base} = 1;
4143     ## TODO: archive
4144     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4145 wakaba 1.1 },
4146 wakaba 1.72 ## NOTE: param*, transparent (Flow)
4147 wakaba 1.41 check_child_element => sub {
4148     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4149     $child_is_transparent, $element_state) = @_;
4150     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4151     $self->{onerror}->(node => $child_el,
4152     type => 'element not allowed:minus',
4153 wakaba 1.104 level => $self->{level}->{must});
4154 wakaba 1.41 $element_state->{has_non_legend} = 1;
4155     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4156     #
4157     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'param') {
4158     if ($element_state->{has_non_param}) {
4159 wakaba 1.104 $self->{onerror}->(node => $child_el,
4160 wakaba 1.72 type => 'element not allowed:flow',
4161 wakaba 1.104 level => $self->{level}->{must});
4162 wakaba 1.39 }
4163 wakaba 1.41 } else {
4164 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
4165 wakaba 1.41 $element_state->{has_non_param} = 1;
4166 wakaba 1.39 }
4167 wakaba 1.25 },
4168 wakaba 1.41 check_child_text => sub {
4169     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4170     if ($has_significant) {
4171     $element_state->{has_non_param} = 1;
4172     }
4173 wakaba 1.42 },
4174     check_end => sub {
4175     my ($self, $item, $element_state) = @_;
4176     if ($element_state->{has_significant}) {
4177 wakaba 1.46 $item->{real_parent_state}->{has_significant} = 1;
4178 wakaba 1.42 } elsif ($item->{node}->manakai_parent_element) {
4179     ## NOTE: Transparent.
4180     } else {
4181     $self->{onerror}->(node => $item->{node},
4182 wakaba 1.104 level => $self->{level}->{should},
4183 wakaba 1.42 type => 'no significant content');
4184     }
4185     },
4186 wakaba 1.8 ## TODO: Tests for <nest/> in <object/>
4187 wakaba 1.1 };
4188 wakaba 1.41 ## ISSUE: Is |<menu><object data><li>aa</li></object></menu>| conforming?
4189     ## What about |<section><object data><style scoped></style>x</object></section>|?
4190     ## |<section><ins></ins><object data><style scoped></style>x</object></section>|?
4191 wakaba 1.1
4192     $Element->{$HTML_NS}->{param} = {
4193 wakaba 1.40 %HTMLEmptyChecker,
4194 wakaba 1.94 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4195 wakaba 1.40 check_attrs => sub {
4196     my ($self, $item, $element_state) = @_;
4197 wakaba 1.1 $GetHTMLAttrsChecker->({
4198     name => sub { },
4199 wakaba 1.70 type => $HTMLIMTAttrChecker,
4200 wakaba 1.1 value => sub { },
4201 wakaba 1.70 valuetype => $GetHTMLEnumeratedAttrChecker->({
4202     data => 1, ref => 1, object => 1,
4203     }),
4204 wakaba 1.49 }, {
4205     %HTMLAttrStatus,
4206 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4207 wakaba 1.94 name => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4208 wakaba 1.82 type => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4209 wakaba 1.94 value => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4210 wakaba 1.82 valuetype => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4211 wakaba 1.66 })->(@_);
4212 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'name')) {
4213     $self->{onerror}->(node => $item->{node},
4214 wakaba 1.104 type => 'attribute missing',
4215     text => 'name',
4216     level => $self->{level}->{must});
4217 wakaba 1.1 }
4218 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'value')) {
4219     $self->{onerror}->(node => $item->{node},
4220 wakaba 1.104 type => 'attribute missing',
4221     text => 'value',
4222     level => $self->{level}->{must});
4223 wakaba 1.1 }
4224     },
4225     };
4226    
4227     $Element->{$HTML_NS}->{video} = {
4228 wakaba 1.40 %HTMLTransparentChecker,
4229 wakaba 1.48 status => FEATURE_HTML5_LC,
4230 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4231 wakaba 1.1 src => $HTMLURIAttrChecker,
4232     ## TODO: start, loopstart, loopend, end
4233     ## ISSUE: they MUST be "value time offset"s. Value?
4234 wakaba 1.11 ## ISSUE: playcount has no conformance creteria
4235 wakaba 1.1 autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
4236     controls => $GetHTMLBooleanAttrChecker->('controls'),
4237 wakaba 1.59 poster => $HTMLURIAttrChecker,
4238 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4239     width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4240 wakaba 1.50 }, {
4241     %HTMLAttrStatus,
4242     autoplay => FEATURE_HTML5_LC,
4243     controls => FEATURE_HTML5_LC,
4244     end => FEATURE_HTML5_LC,
4245     height => FEATURE_HTML5_LC,
4246     loopend => FEATURE_HTML5_LC,
4247     loopstart => FEATURE_HTML5_LC,
4248     playcount => FEATURE_HTML5_LC,
4249     poster => FEATURE_HTML5_LC,
4250     src => FEATURE_HTML5_LC,
4251     start => FEATURE_HTML5_LC,
4252     width => FEATURE_HTML5_LC,
4253 wakaba 1.1 }),
4254 wakaba 1.42 check_start => sub {
4255     my ($self, $item, $element_state) = @_;
4256     $element_state->{allow_source}
4257     = not $item->{node}->has_attribute_ns (undef, 'src');
4258     $element_state->{has_source} ||= $element_state->{allow_source} * -1;
4259     ## NOTE: It might be set true by |check_element|.
4260 wakaba 1.66
4261     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4262     $element_state->{uri_info}->{poster}->{type}->{embedded} = 1;
4263 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4264     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4265 wakaba 1.42 },
4266     check_child_element => sub {
4267     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4268     $child_is_transparent, $element_state) = @_;
4269     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4270     $self->{onerror}->(node => $child_el,
4271     type => 'element not allowed:minus',
4272 wakaba 1.104 level => $self->{level}->{must});
4273 wakaba 1.42 delete $element_state->{allow_source};
4274     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4275     #
4276     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'source') {
4277 wakaba 1.45 unless ($element_state->{allow_source}) {
4278 wakaba 1.104 $self->{onerror}->(node => $child_el,
4279 wakaba 1.72 type => 'element not allowed:flow',
4280 wakaba 1.104 level => $self->{level}->{must});
4281 wakaba 1.42 }
4282 wakaba 1.45 $element_state->{has_source} = 1;
4283 wakaba 1.1 } else {
4284 wakaba 1.42 delete $element_state->{allow_source};
4285 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
4286 wakaba 1.42 }
4287     },
4288     check_child_text => sub {
4289     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4290     if ($has_significant) {
4291     delete $element_state->{allow_source};
4292     }
4293 wakaba 1.72 $HTMLFlowContentChecker{check_child_text}->(@_);
4294 wakaba 1.42 },
4295     check_end => sub {
4296     my ($self, $item, $element_state) = @_;
4297     if ($element_state->{has_source} == -1) {
4298     $self->{onerror}->(node => $item->{node},
4299 wakaba 1.104 type => 'child element missing',
4300     text => 'source',
4301     level => $self->{level}->{must});
4302 wakaba 1.1 }
4303 wakaba 1.42
4304     $Element->{$HTML_NS}->{object}->{check_end}->(@_);
4305 wakaba 1.1 },
4306     };
4307    
4308     $Element->{$HTML_NS}->{audio} = {
4309 wakaba 1.40 %{$Element->{$HTML_NS}->{video}},
4310 wakaba 1.48 status => FEATURE_HTML5_LC,
4311 wakaba 1.42 check_attrs => $GetHTMLAttrsChecker->({
4312     src => $HTMLURIAttrChecker,
4313     ## TODO: start, loopstart, loopend, end
4314     ## ISSUE: they MUST be "value time offset"s. Value?
4315     ## ISSUE: playcount has no conformance creteria
4316     autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
4317     controls => $GetHTMLBooleanAttrChecker->('controls'),
4318 wakaba 1.50 }, {
4319     %HTMLAttrStatus,
4320     autoplay => FEATURE_HTML5_LC,
4321     controls => FEATURE_HTML5_LC,
4322     end => FEATURE_HTML5_LC,
4323     loopend => FEATURE_HTML5_LC,
4324     loopstart => FEATURE_HTML5_LC,
4325     playcount => FEATURE_HTML5_LC,
4326     src => FEATURE_HTML5_LC,
4327     start => FEATURE_HTML5_LC,
4328 wakaba 1.42 }),
4329 wakaba 1.1 };
4330    
4331     $Element->{$HTML_NS}->{source} = {
4332 wakaba 1.40 %HTMLEmptyChecker,
4333 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
4334 wakaba 1.40 check_attrs => sub {
4335     my ($self, $item, $element_state) = @_;
4336 wakaba 1.1 $GetHTMLAttrsChecker->({
4337 wakaba 1.90 media => $HTMLMQAttrChecker,
4338     pixelratio => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
4339     src => $HTMLURIAttrChecker, ## ISSUE: Negative or zero pixelratio=""
4340 wakaba 1.1 type => $HTMLIMTAttrChecker,
4341 wakaba 1.50 }, {
4342     %HTMLAttrStatus,
4343     media => FEATURE_HTML5_DEFAULT,
4344 wakaba 1.90 pixelratio => FEATURE_HTML5_DEFAULT,
4345 wakaba 1.50 src => FEATURE_HTML5_DEFAULT,
4346     type => FEATURE_HTML5_DEFAULT,
4347 wakaba 1.66 })->(@_);
4348 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'src')) {
4349     $self->{onerror}->(node => $item->{node},
4350 wakaba 1.104 type => 'attribute missing',
4351     text => 'src',
4352     level => $self->{level}->{must});
4353 wakaba 1.1 }
4354 wakaba 1.66
4355     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4356 wakaba 1.1 },
4357     };
4358    
4359     $Element->{$HTML_NS}->{canvas} = {
4360 wakaba 1.40 %HTMLTransparentChecker,
4361 wakaba 1.89 status => FEATURE_HTML5_COMPLETE,
4362 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4363 wakaba 1.1 height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4364     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4365 wakaba 1.50 }, {
4366     %HTMLAttrStatus,
4367 wakaba 1.89 height => FEATURE_HTML5_COMPLETE,
4368     width => FEATURE_HTML5_COMPLETE,
4369 wakaba 1.1 }),
4370     };
4371    
4372     $Element->{$HTML_NS}->{map} = {
4373 wakaba 1.72 %HTMLFlowContentChecker,
4374 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4375 wakaba 1.40 check_attrs => sub {
4376     my ($self, $item, $element_state) = @_;
4377 wakaba 1.4 my $has_id;
4378 wakaba 1.100 my $has_name;
4379 wakaba 1.4 $GetHTMLAttrsChecker->({
4380     id => sub {
4381 wakaba 1.100 ## NOTE: Same as the global |id=""|, with |$self->{map}| registration.
4382 wakaba 1.4 my ($self, $attr) = @_;
4383     my $value = $attr->value;
4384 wakaba 1.100 if (length $value) {
4385 wakaba 1.4 if ($self->{id}->{$value}) {
4386 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'duplicate ID',
4387     level => $self->{level}->{must});
4388 wakaba 1.4 push @{$self->{id}->{$value}}, $attr;
4389     } else {
4390     $self->{id}->{$value} = [$attr];
4391     }
4392 wakaba 1.1 } else {
4393 wakaba 1.4 ## NOTE: MUST contain at least one character
4394 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'empty attribute value',
4395     level => $self->{level}->{must});
4396 wakaba 1.1 }
4397 wakaba 1.4 if ($value =~ /[\x09-\x0D\x20]/) {
4398 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'space in ID',
4399     level => $self->{level}->{must});
4400 wakaba 1.4 }
4401 wakaba 1.100 #$self->{map}->{$value} ||= $attr;
4402     $has_id = [$value, $attr];
4403     },
4404     name => sub {
4405     my ($self, $attr) = @_;
4406     my $value = $attr->value;
4407     if (length $value) {
4408     ## NOTE: Duplication is not non-conforming.
4409     ## NOTE: Space characters are not non-conforming.
4410     #
4411     } else {
4412     $self->{onerror}->(node => $attr,
4413     type => 'empty attribute value',
4414 wakaba 1.104 level => $self->{level}->{must});
4415 wakaba 1.100 }
4416 wakaba 1.4 $self->{map}->{$value} ||= $attr;
4417 wakaba 1.100 $has_name = [$value, $attr];
4418 wakaba 1.4 },
4419 wakaba 1.49 }, {
4420     %HTMLAttrStatus,
4421 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4422     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4423     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4424     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4425 wakaba 1.100 #name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
4426     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4427 wakaba 1.50 onclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4428     ondblclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4429     onmousedown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4430     onmouseup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4431     onmouseover => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4432     onmousemove => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4433     onmouseout => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4434     onkeypress => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4435     onkeydown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4436     onkeyup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4437     title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4438 wakaba 1.66 })->(@_);
4439 wakaba 1.100
4440     if ($has_name and $has_id) {
4441     if ($has_name->[0] ne $has_id->[0]) {
4442     $self->{onerror}->(node => $has_id->[1],
4443 wakaba 1.104 type => 'id ne name',
4444     level => $self->{level}->{must});
4445 wakaba 1.100 }
4446     } elsif (not $has_name) {
4447     $self->{onerror}->(node => $item->{node},
4448 wakaba 1.104 type => 'attribute missing',
4449     text => 'name',
4450     level => $self->{level}->{must});
4451 wakaba 1.100 }
4452 wakaba 1.4 },
4453 wakaba 1.59 check_start => sub {
4454     my ($self, $item, $element_state) = @_;
4455     $element_state->{in_map_original} = $self->{flag}->{in_map};
4456     $self->{flag}->{in_map} = 1;
4457 wakaba 1.79
4458     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4459     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4460 wakaba 1.59 },
4461     check_end => sub {
4462     my ($self, $item, $element_state) = @_;
4463     delete $self->{flag}->{in_map} unless $element_state->{in_map_original};
4464 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
4465 wakaba 1.59 },
4466 wakaba 1.1 };
4467    
4468     $Element->{$HTML_NS}->{area} = {
4469 wakaba 1.40 %HTMLEmptyChecker,
4470 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4471 wakaba 1.40 check_attrs => sub {
4472     my ($self, $item, $element_state) = @_;
4473 wakaba 1.1 my %attr;
4474     my $coords;
4475 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
4476 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
4477     $attr_ns = '' unless defined $attr_ns;
4478     my $attr_ln = $attr->manakai_local_name;
4479     my $checker;
4480 wakaba 1.73 my $status;
4481 wakaba 1.1 if ($attr_ns eq '') {
4482 wakaba 1.73 $status = {
4483     %HTMLAttrStatus,
4484     %HTMLM12NCommonAttrStatus,
4485     accesskey => FEATURE_M12N10_REC,
4486     alt => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4487     coords => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4488 wakaba 1.99 href => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_LC | FEATURE_M12N10_REC,
4489 wakaba 1.73 hreflang => FEATURE_HTML5_DEFAULT,
4490     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4491     media => FEATURE_HTML5_DEFAULT,
4492     nohref => FEATURE_M12N10_REC,
4493     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4494     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4495     ping => FEATURE_HTML5_DEFAULT,
4496 wakaba 1.99 rel => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_LC,
4497 wakaba 1.73 shape => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4498     tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4499     target => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4500     type => FEATURE_HTML5_DEFAULT,
4501     }->{$attr_ln};
4502    
4503 wakaba 1.1 $checker = {
4504 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4505 wakaba 1.1 alt => sub { },
4506     ## NOTE: |alt| value has no conformance creteria.
4507     shape => $GetHTMLEnumeratedAttrChecker->({
4508     circ => -1, circle => 1,
4509     default => 1,
4510     poly => 1, polygon => -1,
4511     rect => 1, rectangle => -1,
4512     }),
4513     coords => sub {
4514     my ($self, $attr) = @_;
4515     my $value = $attr->value;
4516     if ($value =~ /\A-?[0-9]+(?>,-?[0-9]+)*\z/) {
4517     $coords = [split /,/, $value];
4518     } else {
4519     $self->{onerror}->(node => $attr,
4520 wakaba 1.104 type => 'coords:syntax error',
4521     level => $self->{level}->{must});
4522 wakaba 1.1 }
4523     },
4524 wakaba 1.70 nohref => $GetHTMLBooleanAttrChecker->('nohref'),
4525     target => $HTMLTargetAttrChecker,
4526 wakaba 1.1 href => $HTMLURIAttrChecker,
4527     ping => $HTMLSpaceURIsAttrChecker,
4528 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) },
4529 wakaba 1.1 media => $HTMLMQAttrChecker,
4530     hreflang => $HTMLLanguageTagAttrChecker,
4531     type => $HTMLIMTAttrChecker,
4532     }->{$attr_ln};
4533     if ($checker) {
4534     $attr{$attr_ln} = $attr;
4535 wakaba 1.117 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/) {
4536 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
4537     $status = $HTMLDatasetAttrStatus;
4538 wakaba 1.1 } else {
4539     $checker = $HTMLAttrChecker->{$attr_ln};
4540     }
4541     }
4542     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
4543 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
4544     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
4545     || $AttrStatus->{$attr_ns}->{''};
4546     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
4547 wakaba 1.62
4548 wakaba 1.1 if ($checker) {
4549 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
4550 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
4551 wakaba 1.54 #
4552 wakaba 1.1 } else {
4553 wakaba 1.104 $self->{onerror}->(node => $attr,
4554     type => 'unknown attribute',
4555     level => $self->{level}->{uncertain});
4556 wakaba 1.1 ## ISSUE: No comformance createria for unknown attributes in the spec
4557     }
4558 wakaba 1.49
4559 wakaba 1.82 $self->_attr_status_info ($attr, $status);
4560 wakaba 1.1 }
4561    
4562     if (defined $attr{href}) {
4563 wakaba 1.4 $self->{has_hyperlink_element} = 1;
4564 wakaba 1.1 unless (defined $attr{alt}) {
4565 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4566 wakaba 1.104 type => 'attribute missing',
4567     text => 'alt',
4568     level => $self->{level}->{must});
4569 wakaba 1.1 }
4570     } else {
4571     for (qw/target ping rel media hreflang type alt/) {
4572     if (defined $attr{$_}) {
4573     $self->{onerror}->(node => $attr{$_},
4574 wakaba 1.104 type => 'attribute not allowed',
4575     level => $self->{level}->{must});
4576 wakaba 1.1 }
4577     }
4578     }
4579    
4580     my $shape = 'rectangle';
4581     if (defined $attr{shape}) {
4582     $shape = {
4583     circ => 'circle', circle => 'circle',
4584     default => 'default',
4585     poly => 'polygon', polygon => 'polygon',
4586     rect => 'rectangle', rectangle => 'rectangle',
4587     }->{lc $attr{shape}->value} || 'rectangle';
4588     ## TODO: ASCII lowercase?
4589     }
4590    
4591     if ($shape eq 'circle') {
4592     if (defined $attr{coords}) {
4593     if (defined $coords) {
4594     if (@$coords == 3) {
4595     if ($coords->[2] < 0) {
4596     $self->{onerror}->(node => $attr{coords},
4597 wakaba 1.104 type => 'coords:out of range',
4598     index => 2,
4599     value => $coords->[2],
4600     level => $self->{level}->{must});
4601 wakaba 1.1 }
4602     } else {
4603     $self->{onerror}->(node => $attr{coords},
4604 wakaba 1.104 type => 'coords:number not 3',
4605     text => 0+@$coords,
4606     level => $self->{level}->{must});
4607 wakaba 1.1 }
4608     } else {
4609     ## NOTE: A syntax error has been reported.
4610     }
4611     } else {
4612 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4613 wakaba 1.104 type => 'attribute missing',
4614     text => 'coords',
4615     level => $self->{level}->{must});
4616 wakaba 1.1 }
4617     } elsif ($shape eq 'default') {
4618     if (defined $attr{coords}) {
4619     $self->{onerror}->(node => $attr{coords},
4620 wakaba 1.104 type => 'attribute not allowed',
4621     level => $self->{level}->{must});
4622 wakaba 1.1 }
4623     } elsif ($shape eq 'polygon') {
4624     if (defined $attr{coords}) {
4625     if (defined $coords) {
4626     if (@$coords >= 6) {
4627     unless (@$coords % 2 == 0) {
4628     $self->{onerror}->(node => $attr{coords},
4629 wakaba 1.104 type => 'coords:number not even',
4630     text => 0+@$coords,
4631     level => $self->{level}->{must});
4632 wakaba 1.1 }
4633     } else {
4634     $self->{onerror}->(node => $attr{coords},
4635 wakaba 1.104 type => 'coords:number lt 6',
4636     text => 0+@$coords,
4637     level => $self->{level}->{must});
4638 wakaba 1.1 }
4639     } else {
4640     ## NOTE: A syntax error has been reported.
4641     }
4642     } else {
4643 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4644 wakaba 1.104 type => 'attribute missing',
4645     text => 'coords',
4646     level => $self->{level}->{must});
4647 wakaba 1.1 }
4648     } elsif ($shape eq 'rectangle') {
4649     if (defined $attr{coords}) {
4650     if (defined $coords) {
4651     if (@$coords == 4) {
4652     unless ($coords->[0] < $coords->[2]) {
4653     $self->{onerror}->(node => $attr{coords},
4654 wakaba 1.104 type => 'coords:out of range',
4655     index => 0,
4656     value => $coords->[0],
4657     level => $self->{level}->{must});
4658 wakaba 1.1 }
4659     unless ($coords->[1] < $coords->[3]) {
4660     $self->{onerror}->(node => $attr{coords},
4661 wakaba 1.104 type => 'coords:out of range',
4662     index => 1,
4663     value => $coords->[1],
4664     level => $self->{level}->{must});
4665 wakaba 1.1 }
4666     } else {
4667     $self->{onerror}->(node => $attr{coords},
4668 wakaba 1.104 type => 'coords:number not 4',
4669     text => 0+@$coords,
4670     level => $self->{level}->{must});
4671 wakaba 1.1 }
4672     } else {
4673     ## NOTE: A syntax error has been reported.
4674     }
4675     } else {
4676 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4677 wakaba 1.104 type => 'attribute missing',
4678     text => 'coords',
4679     level => $self->{level}->{must});
4680 wakaba 1.1 }
4681     }
4682 wakaba 1.66
4683     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
4684 wakaba 1.1 },
4685 wakaba 1.59 check_start => sub {
4686     my ($self, $item, $element_state) = @_;
4687     unless ($self->{flag}->{in_map} or
4688     not $item->{node}->manakai_parent_element) {
4689     $self->{onerror}->(node => $item->{node},
4690     type => 'element not allowed:area',
4691 wakaba 1.104 level => $self->{level}->{must});
4692 wakaba 1.59 }
4693 wakaba 1.79
4694     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4695     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4696 wakaba 1.59 },
4697 wakaba 1.1 };
4698    
4699     $Element->{$HTML_NS}->{table} = {
4700 wakaba 1.40 %HTMLChecker,
4701 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4702 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4703 wakaba 1.86 cellpadding => $HTMLLengthAttrChecker,
4704     cellspacing => $HTMLLengthAttrChecker,
4705 wakaba 1.69 frame => $GetHTMLEnumeratedAttrChecker->({
4706     void => 1, above => 1, below => 1, hsides => 1, vsides => 1,
4707     lhs => 1, rhs => 1, box => 1, border => 1,
4708     }),
4709     rules => $GetHTMLEnumeratedAttrChecker->({
4710     none => 1, groups => 1, rows => 1, cols => 1, all => 1,
4711     }),
4712     summary => sub {}, ## NOTE: %Text; in HTML4.
4713     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## %Pixels;
4714     }, {
4715 wakaba 1.49 %HTMLAttrStatus,
4716 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4717 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
4718     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
4719     border => FEATURE_M12N10_REC,
4720     cellpadding => FEATURE_M12N10_REC,
4721     cellspacing => FEATURE_M12N10_REC,
4722 wakaba 1.61 cols => FEATURE_RFC1942,
4723 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
4724     dataformatas => FEATURE_HTML4_REC_RESERVED,
4725     datapagesize => FEATURE_M12N10_REC,
4726     datasrc => FEATURE_HTML4_REC_RESERVED,
4727     frame => FEATURE_M12N10_REC,
4728 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4729 wakaba 1.49 rules => FEATURE_M12N10_REC,
4730     summary => FEATURE_M12N10_REC,
4731     width => FEATURE_M12N10_REC,
4732     }),
4733 wakaba 1.40 check_start => sub {
4734     my ($self, $item, $element_state) = @_;
4735     $element_state->{phase} = 'before caption';
4736 wakaba 1.66
4737     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4738 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4739     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4740 wakaba 1.40 },
4741     check_child_element => sub {
4742     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4743     $child_is_transparent, $element_state) = @_;
4744     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4745     $self->{onerror}->(node => $child_el,
4746     type => 'element not allowed:minus',
4747 wakaba 1.104 level => $self->{level}->{must});
4748 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4749     #
4750     } elsif ($element_state->{phase} eq 'in tbodys') {
4751     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4752     #$element_state->{phase} = 'in tbodys';
4753     } elsif (not $element_state->{has_tfoot} and
4754     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4755     $element_state->{phase} = 'after tfoot';
4756     $element_state->{has_tfoot} = 1;
4757     } else {
4758 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4759     level => $self->{level}->{must});
4760 wakaba 1.40 }
4761     } elsif ($element_state->{phase} eq 'in trs') {
4762     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4763     #$element_state->{phase} = 'in trs';
4764     } elsif (not $element_state->{has_tfoot} and
4765     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4766     $element_state->{phase} = 'after tfoot';
4767     $element_state->{has_tfoot} = 1;
4768     } else {
4769 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4770     level => $self->{level}->{must});
4771 wakaba 1.40 }
4772     } elsif ($element_state->{phase} eq 'after thead') {
4773     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4774     $element_state->{phase} = 'in tbodys';
4775     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4776     $element_state->{phase} = 'in trs';
4777     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4778     $element_state->{phase} = 'in tbodys';
4779     $element_state->{has_tfoot} = 1;
4780     } else {
4781 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4782     level => $self->{level}->{must});
4783 wakaba 1.40 }
4784     } elsif ($element_state->{phase} eq 'in colgroup') {
4785     if ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
4786     $element_state->{phase} = 'in colgroup';
4787     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
4788     $element_state->{phase} = 'after thead';
4789     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4790     $element_state->{phase} = 'in tbodys';
4791     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4792     $element_state->{phase} = 'in trs';
4793     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4794     $element_state->{phase} = 'in tbodys';
4795     $element_state->{has_tfoot} = 1;
4796     } else {
4797 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4798     level => $self->{level}->{must});
4799 wakaba 1.40 }
4800     } elsif ($element_state->{phase} eq 'before caption') {
4801     if ($child_nsuri eq $HTML_NS and $child_ln eq 'caption') {
4802     $element_state->{phase} = 'in colgroup';
4803     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
4804     $element_state->{phase} = 'in colgroup';
4805     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
4806     $element_state->{phase} = 'after thead';
4807     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4808     $element_state->{phase} = 'in tbodys';
4809     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4810     $element_state->{phase} = 'in trs';
4811     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4812     $element_state->{phase} = 'in tbodys';
4813     $element_state->{has_tfoot} = 1;
4814     } else {
4815 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4816     level => $self->{level}->{must});
4817 wakaba 1.40 }
4818     } elsif ($element_state->{phase} eq 'after tfoot') {
4819 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4820     level => $self->{level}->{must});
4821 wakaba 1.40 } else {
4822     die "check_child_element: Bad |table| phase: $element_state->{phase}";
4823     }
4824     },
4825     check_child_text => sub {
4826     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4827     if ($has_significant) {
4828 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
4829     level => $self->{level}->{must});
4830 wakaba 1.1 }
4831 wakaba 1.40 },
4832     check_end => sub {
4833     my ($self, $item, $element_state) = @_;
4834 wakaba 1.1
4835     ## Table model errors
4836     require Whatpm::HTMLTable;
4837 wakaba 1.87 my $table = Whatpm::HTMLTable->form_table ($item->{node}, sub {
4838 wakaba 1.104 $self->{onerror}->(@_);
4839     }, $self->{level});
4840 wakaba 1.87 Whatpm::HTMLTable->assign_header
4841 wakaba 1.104 ($table, $self->{onerror}, $self->{level});
4842 wakaba 1.87 push @{$self->{return}->{table}}, $table;
4843 wakaba 1.1
4844 wakaba 1.40 $HTMLChecker{check_end}->(@_);
4845 wakaba 1.1 },
4846     };
4847    
4848     $Element->{$HTML_NS}->{caption} = {
4849 wakaba 1.40 %HTMLPhrasingContentChecker,
4850 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4851 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4852     align => $GetHTMLEnumeratedAttrChecker->({
4853     top => 1, bottom => 1, left => 1, right => 1,
4854     }),
4855     }, {
4856 wakaba 1.49 %HTMLAttrStatus,
4857 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4858 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
4859 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4860 wakaba 1.49 }),
4861 wakaba 1.1 };
4862    
4863 wakaba 1.69 my %cellalign = (
4864     ## HTML4 %cellhalign;
4865 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
4866     left => 1, center => 1, right => 1, justify => 1, char => 1,
4867     }),
4868     char => sub {
4869     my ($self, $attr) = @_;
4870 wakaba 1.69
4871 wakaba 1.70 ## NOTE: "character" or |%Character;| in HTML4.
4872    
4873     my $value = $attr->value;
4874     if (length $value != 1) {
4875     $self->{onerror}->(node => $attr, type => 'char:syntax error',
4876 wakaba 1.105 level => $self->{level}->{html4_fact});
4877 wakaba 1.70 }
4878     },
4879 wakaba 1.86 charoff => $HTMLLengthAttrChecker,
4880    
4881 wakaba 1.69 ## HTML4 %cellvalign;
4882 wakaba 1.70 valign => $GetHTMLEnumeratedAttrChecker->({
4883     top => 1, middle => 1, bottom => 1, baseline => 1,
4884     }),
4885 wakaba 1.69 );
4886    
4887 wakaba 1.1 $Element->{$HTML_NS}->{colgroup} = {
4888 wakaba 1.40 %HTMLEmptyChecker,
4889 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4890 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4891 wakaba 1.69 %cellalign,
4892 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4893     ## NOTE: Defined only if "the |colgroup| element contains no |col| elements"
4894     ## TODO: "attribute not supported" if |col|.
4895     ## ISSUE: MUST NOT if any |col|?
4896     ## ISSUE: MUST NOT for |<colgroup span="1"><any><col/></any></colgroup>| (though non-conforming)?
4897 wakaba 1.49 }, {
4898     %HTMLAttrStatus,
4899 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4900 wakaba 1.49 align => FEATURE_M12N10_REC,
4901     char => FEATURE_M12N10_REC,
4902     charoff => FEATURE_M12N10_REC,
4903 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4904 wakaba 1.82 span => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4905 wakaba 1.49 valign => FEATURE_M12N10_REC,
4906     width => FEATURE_M12N10_REC,
4907 wakaba 1.1 }),
4908 wakaba 1.40 check_child_element => sub {
4909     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4910     $child_is_transparent, $element_state) = @_;
4911     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4912     $self->{onerror}->(node => $child_el,
4913     type => 'element not allowed:minus',
4914 wakaba 1.104 level => $self->{level}->{must});
4915 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4916     #
4917     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'col') {
4918     #
4919     } else {
4920 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4921     level => $self->{level}->{must});
4922 wakaba 1.40 }
4923     },
4924     check_child_text => sub {
4925     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4926     if ($has_significant) {
4927 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
4928     level => $self->{level}->{must});
4929 wakaba 1.1 }
4930     },
4931     };
4932    
4933     $Element->{$HTML_NS}->{col} = {
4934 wakaba 1.40 %HTMLEmptyChecker,
4935 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4936 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4937 wakaba 1.69 %cellalign,
4938 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4939 wakaba 1.49 }, {
4940     %HTMLAttrStatus,
4941 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4942 wakaba 1.49 align => FEATURE_M12N10_REC,
4943     char => FEATURE_M12N10_REC,
4944     charoff => FEATURE_M12N10_REC,
4945 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4946 wakaba 1.82 span => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4947 wakaba 1.49 valign => FEATURE_M12N10_REC,
4948     width => FEATURE_M12N10_REC,
4949 wakaba 1.1 }),
4950     };
4951    
4952     $Element->{$HTML_NS}->{tbody} = {
4953 wakaba 1.40 %HTMLChecker,
4954 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4955 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4956     %cellalign,
4957     }, {
4958 wakaba 1.49 %HTMLAttrStatus,
4959 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4960 wakaba 1.49 align => FEATURE_M12N10_REC,
4961     char => FEATURE_M12N10_REC,
4962     charoff => FEATURE_M12N10_REC,
4963 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4964 wakaba 1.49 valign => FEATURE_M12N10_REC,
4965     }),
4966 wakaba 1.40 check_child_element => sub {
4967     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4968     $child_is_transparent, $element_state) = @_;
4969     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4970     $self->{onerror}->(node => $child_el,
4971     type => 'element not allowed:minus',
4972 wakaba 1.104 level => $self->{level}->{must});
4973 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4974     #
4975     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4976 wakaba 1.84 #
4977 wakaba 1.40 } else {
4978 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4979     level => $self->{level}->{must});
4980 wakaba 1.40 }
4981     },
4982     check_child_text => sub {
4983     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4984     if ($has_significant) {
4985 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
4986     level => $self->{level}->{must});
4987 wakaba 1.1 }
4988 wakaba 1.40 },
4989 wakaba 1.1 };
4990    
4991     $Element->{$HTML_NS}->{thead} = {
4992 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
4993 wakaba 1.1 };
4994    
4995     $Element->{$HTML_NS}->{tfoot} = {
4996 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
4997 wakaba 1.1 };
4998    
4999     $Element->{$HTML_NS}->{tr} = {
5000 wakaba 1.40 %HTMLChecker,
5001 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5002 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
5003     %cellalign,
5004     bgcolor => $HTMLColorAttrChecker,
5005     }, {
5006 wakaba 1.49 %HTMLAttrStatus,
5007 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5008 wakaba 1.49 align => FEATURE_M12N10_REC,
5009     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
5010     char => FEATURE_M12N10_REC,
5011     charoff => FEATURE_M12N10_REC,
5012 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5013 wakaba 1.49 valign => FEATURE_M12N10_REC,
5014     }),
5015 wakaba 1.40 check_child_element => sub {
5016     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5017     $child_is_transparent, $element_state) = @_;
5018     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5019     $self->{onerror}->(node => $child_el,
5020     type => 'element not allowed:minus',
5021 wakaba 1.104 level => $self->{level}->{must});
5022 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5023     #
5024     } elsif ($child_nsuri eq $HTML_NS and
5025     ($child_ln eq 'td' or $child_ln eq 'th')) {
5026 wakaba 1.84 #
5027 wakaba 1.40 } else {
5028 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5029     level => $self->{level}->{must});
5030 wakaba 1.40 }
5031     },
5032     check_child_text => sub {
5033     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5034     if ($has_significant) {
5035 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
5036     level => $self->{level}->{must});
5037 wakaba 1.1 }
5038     },
5039     };
5040    
5041     $Element->{$HTML_NS}->{td} = {
5042 wakaba 1.72 %HTMLFlowContentChecker,
5043 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5044 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5045 wakaba 1.69 %cellalign,
5046     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
5047     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
5048     bgcolor => $HTMLColorAttrChecker,
5049 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5050 wakaba 1.87 headers => sub {
5051     ## NOTE: Will be checked by Whatpm::HTMLTable->assign_header.
5052     ## Though that method does not check the |headers| attribute of a
5053     ## |td| element if the element does not form a table, in that case
5054     ## the |td| element is non-conforming anyway.
5055     },
5056 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
5057 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5058 wakaba 1.69 scope => $GetHTMLEnumeratedAttrChecker
5059     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
5060 wakaba 1.49 }, {
5061     %HTMLAttrStatus,
5062 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5063     abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5064 wakaba 1.49 align => FEATURE_M12N10_REC,
5065 wakaba 1.82 axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5066 wakaba 1.49 bgcolor => FEATURE_M12N10_REC_DEPRECATED,
5067     char => FEATURE_M12N10_REC,
5068     charoff => FEATURE_M12N10_REC,
5069 wakaba 1.82 colspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5070 wakaba 1.87 headers => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5071 wakaba 1.49 height => FEATURE_M12N10_REC_DEPRECATED,
5072 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5073 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
5074 wakaba 1.82 rowspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5075     scope => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5076 wakaba 1.49 valign => FEATURE_M12N10_REC,
5077     width => FEATURE_M12N10_REC_DEPRECATED,
5078 wakaba 1.1 }),
5079     };
5080    
5081     $Element->{$HTML_NS}->{th} = {
5082 wakaba 1.40 %HTMLPhrasingContentChecker,
5083 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5084 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5085 wakaba 1.69 %cellalign,
5086     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
5087     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
5088     bgcolor => $HTMLColorAttrChecker,
5089 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5090 wakaba 1.87 ## TODO: HTML4(?) |headers|
5091 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
5092 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5093     scope => $GetHTMLEnumeratedAttrChecker
5094     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
5095 wakaba 1.49 }, {
5096     %HTMLAttrStatus,
5097 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5098     abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5099 wakaba 1.49 align => FEATURE_M12N10_REC,
5100 wakaba 1.82 axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5101 wakaba 1.49 bgcolor => FEATURE_M12N10_REC_DEPRECATED,
5102     char => FEATURE_M12N10_REC,
5103     charoff => FEATURE_M12N10_REC,
5104 wakaba 1.82 colspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5105     headers => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5106 wakaba 1.49 height => FEATURE_M12N10_REC_DEPRECATED,
5107 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5108 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
5109 wakaba 1.82 rowspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5110     scope => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5111 wakaba 1.49 valign => FEATURE_M12N10_REC,
5112     width => FEATURE_M12N10_REC_DEPRECATED,
5113 wakaba 1.1 }),
5114     };
5115    
5116 wakaba 1.52 $Element->{$HTML_NS}->{form} = {
5117 wakaba 1.72 %HTMLFlowContentChecker, ## NOTE: Flow* [WF2]
5118 wakaba 1.56 ## TODO: form in form is allowed in XML [WF2]
5119 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
5120     check_attrs => $GetHTMLAttrsChecker->({
5121 wakaba 1.56 accept => $AttrCheckerNotImplemented, ## TODO: ContentTypes [WF2]
5122 wakaba 1.52 'accept-charset' => $AttrCheckerNotImplemented, ## TODO: Charsets
5123     action => $HTMLURIAttrChecker, ## TODO: "User agent behavior for a value other than HTTP URI is undefined" [HTML4]
5124 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
5125     enctype => $HTMLIMTAttrChecker, ## TODO: "multipart/form-data" should be used when type=file is used [HTML4] ## TODO: MUST NOT parameter [WF2]
5126     method => $GetHTMLEnumeratedAttrChecker->({
5127     get => 1, post => 1, put => 1, delete => 1,
5128     }),
5129 wakaba 1.52 ## NOTE: "get" SHOULD be used for idempotent submittion,
5130     ## "post" SHOULD be used otherwise [HTML4]. This cannot be tested.
5131     name => sub { }, # CDATA in HTML4 ## TODO: must be same as |id| (informative!) [XHTML10]
5132 wakaba 1.56 onreceived => $HTMLEventHandlerAttrChecker,
5133     replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
5134 wakaba 1.52 target => $HTMLTargetAttrChecker,
5135     ## TODO: Warn for combination whose behavior is not defined.
5136     }, {
5137     %HTMLAttrStatus,
5138     %HTMLM12NCommonAttrStatus,
5139 wakaba 1.56 accept => FEATURE_WF2 | FEATURE_M12N10_REC,
5140 wakaba 1.52 'accept-charset' => FEATURE_M12N10_REC,
5141 wakaba 1.56 action => FEATURE_WF2 | FEATURE_M12N10_REC,
5142     data => FEATURE_WF2,
5143     enctype => FEATURE_WF2 | FEATURE_M12N10_REC,
5144 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5145 wakaba 1.56 method => FEATURE_WF2 | FEATURE_M12N10_REC,
5146 wakaba 1.52 name => FEATURE_M12N10_REC_DEPRECATED,
5147 wakaba 1.56 onreceived => FEATURE_WF2,
5148 wakaba 1.52 onreset => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5149     onsubmit => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5150 wakaba 1.56 replace => FEATURE_WF2,
5151 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
5152     sdasuff => FEATURE_HTML20_RFC,
5153 wakaba 1.52 target => FEATURE_M12N10_REC,
5154     }),
5155     ## TODO: Tests
5156     ## TODO: Tests for <nest/> in <form>
5157 wakaba 1.66 check_start => sub {
5158     my ($self, $item, $element_state) = @_;
5159    
5160     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5161     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5162 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5163     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5164 wakaba 1.66 },
5165 wakaba 1.52 };
5166    
5167     $Element->{$HTML_NS}->{fieldset} = {
5168 wakaba 1.72 %HTMLFlowContentChecker, ## NOTE: legend, %Flow; ## TODO: legend
5169 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
5170 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
5171     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5172     ## TODO: form [WF2]
5173     }, {
5174 wakaba 1.52 %HTMLAttrStatus,
5175     %HTMLM12NCommonAttrStatus,
5176 wakaba 1.56 disabled => FEATURE_WF2,
5177     form => FEATURE_WF2,
5178 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5179     }),
5180     ## TODO: Tests
5181     ## TODO: Tests for <nest/> in <fieldset>
5182     };
5183    
5184     $Element->{$HTML_NS}->{input} = {
5185 wakaba 1.56 %HTMLEmptyChecker, ## MUST [WF2]
5186 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
5187     check_attrs => $GetHTMLAttrsChecker->({
5188 wakaba 1.56 accept => $AttrCheckerNotImplemented, ## TODO: ContentTypes [WF2]
5189 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5190 wakaba 1.56 action => $HTMLURIAttrChecker,
5191 wakaba 1.52 align => $GetHTMLEnumeratedAttrChecker->({
5192     top => 1, middle => 1, bottom => 1, left => 1, right => 1,
5193     }),
5194     alt => sub {}, ## NOTE: Text [M12N] ## TODO: |alt| should be provided for |type=image| [HTML4]
5195     ## NOTE: HTML4 has a "should" for accessibility, which cannot be tested
5196     ## here.
5197 wakaba 1.56 autocomplete => $GetHTMLEnumeratedAttrChecker->({on => 1, off => 1}),
5198     autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
5199 wakaba 1.52 checked => $GetHTMLBooleanAttrChecker->('checked'),
5200     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5201 wakaba 1.56 enctype => $HTMLIMTAttrChecker,
5202     ## TODO: form [WF2]
5203     ## TODO: inputmode [WF2]
5204 wakaba 1.52 ismap => $GetHTMLBooleanAttrChecker->('ismap'),
5205 wakaba 1.56 ## TODO: list [WF2]
5206     ## TODO: max [WF2]
5207 wakaba 1.52 maxlength => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5208 wakaba 1.56 method => $GetHTMLEnumeratedAttrChecker->({
5209     get => 1, post => 1, put => 1, delete => 1,
5210     }),
5211     ## TODO: min [WF2]
5212 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
5213     readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5214 wakaba 1.56 replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
5215     required => $GetHTMLBooleanAttrChecker->('required'),
5216 wakaba 1.52 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5217     src => $HTMLURIAttrChecker,
5218 wakaba 1.56 ## TODO: step [WF2]
5219     target => $HTMLTargetAttrChecker,
5220 wakaba 1.80 ## NOTE: According to Web Forms 2.0, |input| attribute has |template|
5221     ## attribute to support the |add| button type (as part of repetition
5222     ## template feature). It conflicts with the |template| global attribute
5223     ## introduced as part of the data template feature.
5224     ## NOTE: |template| attribute as defined in Web Forms 2.0 has no
5225     ## author requirement.
5226 wakaba 1.52 type => $GetHTMLEnumeratedAttrChecker->({
5227     text => 1, password => 1, checkbox => 1, radio => 1, submit => 1,
5228     reset => 1, file => 1, hidden => 1, image => 1, button => 1,
5229 wakaba 1.56 ## [WF2]
5230     datatime => 1, 'datetime-local' => 1, date => 1, month => 1, week => 1,
5231     time => 1, number => 1, range => 1, email => 1, url => 1,
5232     add => 1, remove => 1, 'move-up' => 1, 'move-down' => 1,
5233 wakaba 1.52 }),
5234     usemap => $HTMLUsemapAttrChecker,
5235 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]
5236     ## TODO: "authors should ensure that in each set of radio buttons that one is initially "on"." [HTML4] [WF2]
5237 wakaba 1.52 }, {
5238     %HTMLAttrStatus,
5239     %HTMLM12NCommonAttrStatus,
5240 wakaba 1.56 accept => FEATURE_WF2 | FEATURE_M12N10_REC,
5241 wakaba 1.61 'accept-charset' => FEATURE_HTML2X_RFC,
5242 wakaba 1.52 accesskey => FEATURE_M12N10_REC,
5243 wakaba 1.56 action => FEATURE_WF2,
5244 wakaba 1.52 align => FEATURE_M12N10_REC_DEPRECATED,
5245     alt => FEATURE_M12N10_REC,
5246 wakaba 1.56 autocomplete => FEATURE_WF2,
5247     autofocus => FEATURE_WF2,
5248 wakaba 1.52 checked => FEATURE_M12N10_REC,
5249     datafld => FEATURE_HTML4_REC_RESERVED,
5250     dataformatas => FEATURE_HTML4_REC_RESERVED,
5251     datasrc => FEATURE_HTML4_REC_RESERVED,
5252 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
5253 wakaba 1.65 enctype => FEATURE_WF2,
5254 wakaba 1.56 form => FEATURE_WF2,
5255     inputmode => FEATURE_WF2 | FEATURE_XHTMLBASIC11_CR,
5256 wakaba 1.52 ismap => FEATURE_M12N10_REC,
5257     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5258 wakaba 1.56 list => FEATURE_WF2,
5259     max => FEATURE_WF2,
5260     maxlength => FEATURE_WF2 | FEATURE_M12N10_REC,
5261     method => FEATURE_WF2,
5262     min => FEATURE_WF2,
5263 wakaba 1.52 name => FEATURE_M12N10_REC,
5264     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5265     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5266     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5267     onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5268 wakaba 1.56 readonly => FEATURE_WF2 | FEATURE_M12N10_REC,
5269 wakaba 1.65 replace => FEATURE_WF2,
5270 wakaba 1.56 required => FEATURE_WF2,
5271 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
5272 wakaba 1.56 size => FEATURE_WF2_DEPRECATED | FEATURE_M12N10_REC,
5273 wakaba 1.52 src => FEATURE_M12N10_REC,
5274 wakaba 1.56 step => FEATURE_WF2,
5275 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5276 wakaba 1.65 target => FEATURE_WF2,
5277 wakaba 1.56 template => FEATURE_WF2,
5278 wakaba 1.52 type => FEATURE_M12N10_REC,
5279     usemap => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC,
5280     value => FEATURE_M12N10_REC,
5281     }),
5282     ## TODO: Tests
5283     ## TODO: Tests for <nest/> in <input>
5284 wakaba 1.66 check_start => sub {
5285     my ($self, $item, $element_state) = @_;
5286    
5287     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5288     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
5289     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
5290 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5291     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5292 wakaba 1.66 },
5293 wakaba 1.52 };
5294    
5295 wakaba 1.56 ## TODO: Form |name| attributes: MUST NOT conflict with RFC 3106 [WF2]
5296    
5297 wakaba 1.80 ## NOTE: "authors who are nesting repetition blocks should position such
5298     ## [repetition-block-related] buttons carefully to make clear which block a
5299     ## button applies to.": I have no idea how this can be tested.
5300    
5301 wakaba 1.52 $Element->{$HTML_NS}->{button} = {
5302 wakaba 1.72 %HTMLFlowContentChecker, ## NOTE: %Flow; - something [XHTML10]
5303 wakaba 1.52 ## TODO: -A|%formctrl;|form|fieldset [HTML4]
5304     ## TODO: image map (img) in |button| is "illegal" [HTML4].
5305     status => FEATURE_WF2 | FEATURE_M12N10_REC,
5306     check_attrs => $GetHTMLAttrsChecker->({
5307 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5308 wakaba 1.56 action => $HTMLURIAttrChecker,
5309     autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
5310 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5311 wakaba 1.56 ## TODO: form [WF2]
5312     method => $GetHTMLEnumeratedAttrChecker->({
5313     get => 1, post => 1, put => 1, delete => 1,
5314     }),
5315 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
5316 wakaba 1.56 oninvalid => $HTMLEventHandlerAttrChecker,
5317     replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
5318     target => $HTMLTargetAttrChecker,
5319 wakaba 1.80 ## NOTE: According to Web Forms 2.0, |button| attribute has |template|
5320     ## attribute to support the |add| button type (as part of repetition
5321     ## template feature). It conflicts with the |template| global attribute
5322     ## introduced as part of the data template feature.
5323     ## NOTE: |template| attribute as defined in Web Forms 2.0 has no
5324     ## author requirement.
5325 wakaba 1.52 type => $GetHTMLEnumeratedAttrChecker->({
5326     button => 1, submit => 1, reset => 1,
5327 wakaba 1.80 ## [WF2]
5328     add => 1, remove => 1, 'move-up' => 1, 'move-down' => 1,
5329 wakaba 1.52 }),
5330     value => sub {}, ## NOTE: CDATA [M12N]
5331     }, {
5332     %HTMLAttrStatus,
5333     %HTMLM12NCommonAttrStatus,
5334     accesskey => FEATURE_M12N10_REC,
5335 wakaba 1.56 action => FEATURE_WF2,
5336     autofocus => FEATURE_WF2,
5337 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
5338     dataformatas => FEATURE_HTML4_REC_RESERVED,
5339     datasrc => FEATURE_HTML4_REC_RESERVED,
5340 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
5341     enctype => FEATURE_WF2,
5342     form => FEATURE_WF2,
5343 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5344 wakaba 1.56 method => FEATURE_WF2,
5345 wakaba 1.52 name => FEATURE_M12N10_REC,
5346     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5347     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5348 wakaba 1.56 oninvalid => FEATURE_WF2,
5349     replace => FEATURE_WF2,
5350 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5351 wakaba 1.56 target => FEATURE_WF2,
5352     template => FEATURE_WF2,
5353 wakaba 1.52 type => FEATURE_M12N10_REC,
5354     value => FEATURE_M12N10_REC,
5355     }),
5356     ## TODO: Tests
5357     ## TODO: Tests for <nest/> in <button>
5358 wakaba 1.66 check_start => sub {
5359     my ($self, $item, $element_state) = @_;
5360    
5361     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5362     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
5363 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5364     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5365 wakaba 1.66 },
5366 wakaba 1.52 };
5367    
5368     $Element->{$HTML_NS}->{label} = {
5369     %HTMLPhrasingContentChecker, ## NOTE: %Inline - label [XHTML10] ## TODO: -label
5370 wakaba 1.56 ## TODO: At most one form control [WF2]
5371 wakaba 1.82 status => FEATURE_WF2 | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5372 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5373 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5374 wakaba 1.52 for => $AttrCheckerNotImplemented, ## TODO: IDREF ## TODO: Must be |id| of control [HTML4] ## TODO: Or, "may only contain one control element"
5375     }, {
5376     %HTMLAttrStatus,
5377 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5378 wakaba 1.56 accesskey => FEATURE_WF2 | FEATURE_M12N10_REC,
5379 wakaba 1.52 for => FEATURE_M12N10_REC,
5380     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5381     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5382     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5383     }),
5384     ## TODO: Tests
5385     ## TODO: Tests for <nest/> in <label>
5386     };
5387    
5388     $Element->{$HTML_NS}->{select} = {
5389 wakaba 1.72 %HTMLFlowContentChecker, ## TODO: (optgroup|option)* [HTML4] + [WF2] ## TODO: SHOULD avoid empty and visible [WF2]
5390 wakaba 1.52 ## TODO: author should SELECTED at least one OPTION in non-MULTIPLE case [HTML4].
5391     ## TODO: more than one OPTION with SELECTED in non-MULTIPLE case is "error" [HTML4]
5392     status => FEATURE_WF2 | FEATURE_M12N10_REC,
5393 wakaba 1.56 is_root => 1, ## TODO: SHOULD NOT in application/xhtml+xml [WF2]
5394 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5395 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5396 wakaba 1.56 autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
5397 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5398 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
5399     ## TODO: form [WF2]
5400 wakaba 1.52 multiple => $GetHTMLBooleanAttrChecker->('multiple'),
5401     name => sub {}, ## NOTE: CDATA [M12N]
5402 wakaba 1.56 oninvalid => $HTMLEventHandlerAttrChecker,
5403     ## TODO: pattern [WF2] ## TODO: |title| semantics
5404 wakaba 1.52 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5405     }, {
5406     %HTMLAttrStatus,
5407     %HTMLM12NCommonAttrStatus,
5408 wakaba 1.56 accesskey => FEATURE_WF2,
5409     autofocus => FEATURE_WF2,
5410     data => FEATURE_WF2,
5411 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
5412     dataformatas => FEATURE_HTML4_REC_RESERVED,
5413     datasrc => FEATURE_HTML4_REC_RESERVED,
5414 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
5415     form => FEATURE_WF2,
5416 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5417     multiple => FEATURE_M12N10_REC,
5418     name => FEATURE_M12N10_REC,
5419     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5420     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5421     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5422 wakaba 1.56 oninvalid => FEATURE_WF2,
5423     pattern => FEATURE_WF2,
5424 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
5425     sdapref => FEATURE_HTML20_RFC,
5426 wakaba 1.52 size => FEATURE_M12N10_REC,
5427     tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5428     }),
5429     ## TODO: Tests
5430     ## TODO: Tests for <nest/> in <select>
5431 wakaba 1.66 check_start => sub {
5432     my ($self, $item, $element_state) = @_;
5433    
5434     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5435     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
5436 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5437     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5438 wakaba 1.66 },
5439 wakaba 1.52 };
5440 wakaba 1.1
5441 wakaba 1.52 $Element->{$HTML_NS}->{datalist} = {
5442 wakaba 1.72 %HTMLFlowContentChecker, ## TODO: (transparent | option)*
5443 wakaba 1.56 ## TODO: |option| child MUST be empty [WF2]
5444 wakaba 1.52 status => FEATURE_WF2,
5445 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
5446     data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
5447     }, {
5448 wakaba 1.52 %HTMLAttrStatus,
5449 wakaba 1.56 data => FEATURE_WF2,
5450 wakaba 1.52 }),
5451     ## TODO: Tests
5452     ## TODO: Tests for <nest/> in <datalist>
5453 wakaba 1.66 check_start => sub {
5454     my ($self, $item, $element_state) = @_;
5455    
5456     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5457 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5458     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5459 wakaba 1.66 },
5460 wakaba 1.52 };
5461 wakaba 1.49
5462 wakaba 1.52 $Element->{$HTML_NS}->{optgroup} = {
5463 wakaba 1.72 %HTMLFlowContentChecker, ## TODO: (option|optgroup)* [HTML4] + [WF2] SHOULD avoid empty and visible [WF2]
5464 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
5465     check_attrs => $GetHTMLAttrsChecker->({
5466     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5467     label => sub {}, ## NOTE: Text [M12N] ## TODO: required
5468     }, {
5469     %HTMLAttrStatus,
5470     %HTMLM12NCommonAttrStatus,
5471 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
5472 wakaba 1.52 label => FEATURE_M12N10_REC,
5473     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5474     }),
5475     ## TODO: Tests
5476     ## TODO: Tests for <nest/> in <optgroup>
5477     };
5478    
5479     $Element->{$HTML_NS}->{option} = {
5480     %HTMLTextChecker,
5481     status => FEATURE_WF2 | FEATURE_M12N10_REC,
5482     check_attrs => $GetHTMLAttrsChecker->({
5483     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5484     label => sub {}, ## NOTE: Text [M12N]
5485     selected => $GetHTMLBooleanAttrChecker->('selected'),
5486     value => sub {}, ## NOTE: CDATA [M12N]
5487     }, {
5488     %HTMLAttrStatus,
5489     %HTMLM12NCommonAttrStatus,
5490 wakaba 1.56 disabled => FEATURE_WF2, FEATURE_M12N10_REC,
5491 wakaba 1.52 label => FEATURE_M12N10_REC,
5492     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5493 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
5494     sdapref => FEATURE_HTML20_RFC,
5495 wakaba 1.52 selected => FEATURE_M12N10_REC,
5496     value => FEATURE_M12N10_REC,
5497     }),
5498     ## TODO: Tests
5499     ## TODO: Tests for <nest/> in <option>
5500     };
5501 wakaba 1.49
5502 wakaba 1.52 $Element->{$HTML_NS}->{textarea} = {
5503     %HTMLTextChecker,
5504     status => FEATURE_WF2 | FEATURE_M12N10_REC,
5505     check_attrs => $GetHTMLAttrsChecker->({
5506 wakaba 1.56 accept => $HTMLIMTAttrChecker, ## TODO: MUST be a text-based type
5507 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5508 wakaba 1.56 autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
5509     cols => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## TODO: SHOULD if wrap=hard [WF2]
5510 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5511 wakaba 1.56 ## TODO: form [WF2]
5512     ## TODO: inputmode [WF2]
5513     maxlength => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5514 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
5515 wakaba 1.56 ## TODO: pattern [WF2] ## TODO: |title| special semantics
5516 wakaba 1.52 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5517 wakaba 1.56 required => $GetHTMLBooleanAttrChecker->('required'),
5518     rows => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5519     oninvalid => $HTMLEventHandlerAttrChecker,
5520     wrap => $GetHTMLEnumeratedAttrChecker->({soft => 1, hard => 1}),
5521 wakaba 1.52 }, {
5522     %HTMLAttrStatus,
5523     %HTMLM12NCommonAttrStatus,
5524 wakaba 1.56 accept => FEATURE_WF2,
5525 wakaba 1.61 'accept-charset' => FEATURE_HTML2X_RFC,
5526 wakaba 1.52 accesskey => FEATURE_M12N10_REC,
5527 wakaba 1.56 autofocus => FEATURE_WF2,
5528 wakaba 1.52 cols => FEATURE_M12N10_REC,
5529     datafld => FEATURE_HTML4_REC_RESERVED,
5530 wakaba 1.49 dataformatas => FEATURE_HTML4_REC_RESERVED,
5531     datasrc => FEATURE_HTML4_REC_RESERVED,
5532 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
5533     form => FEATURE_WF2,
5534     inputmode => FEATURE_WF2 | FEATURE_XHTMLBASIC11_CR,
5535 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5536 wakaba 1.56 maxlength => FEATURE_WF2,
5537 wakaba 1.52 name => FEATURE_M12N10_REC,
5538     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5539     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5540     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5541 wakaba 1.56 oninvalid => FEATURE_WF2,
5542 wakaba 1.52 onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5543 wakaba 1.56 pattern => FEATURE_WF2,
5544     readonly => FEATURE_WF2 | FEATURE_M12N10_REC,
5545     required => FEATURE_WF2,
5546 wakaba 1.61 rows => FEATURE_M12N10_REC,
5547     sdaform => FEATURE_HTML20_RFC,
5548     sdapref => FEATURE_HTML20_RFC,
5549 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5550 wakaba 1.56 wrap => FEATURE_WF2,
5551 wakaba 1.52 }),
5552     ## TODO: Tests
5553     ## TODO: Tests for <nest/> in <textarea>
5554 wakaba 1.66 check_start => sub {
5555     my ($self, $item, $element_state) = @_;
5556    
5557     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5558 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5559     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5560 wakaba 1.66 },
5561 wakaba 1.52 };
5562 wakaba 1.49
5563 wakaba 1.52 $Element->{$HTML_NS}->{output} = {
5564 wakaba 1.56 %HTMLPhrasingContentChecker, ## Inline [WF2]
5565 wakaba 1.52 status => FEATURE_WF2,
5566 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
5567     ## TODO: for [WF2]
5568     ## TODO: form [WF2]
5569     ## TODO: name [WF2]
5570     ## onformchange[WF2]
5571     ## onforminput[WF2]
5572     }, {
5573 wakaba 1.52 %HTMLAttrStatus,
5574 wakaba 1.56 for => FEATURE_WF2,
5575     form => FEATURE_WF2,
5576     name => FEATURE_WF2,
5577     onchange => FEATURE_HTML5_DEFAULT | FEATURE_WF2,
5578     onformchange => FEATURE_WF2,
5579     onforminput => FEATURE_WF2,
5580 wakaba 1.52 }),
5581     ## TODO: Tests
5582     ## TODO: Tests for <nest/> in <output>
5583 wakaba 1.56 ## NOTE: "The output element should be used when ..." [WF2]
5584 wakaba 1.52 };
5585    
5586     $Element->{$HTML_NS}->{isindex} = {
5587     %HTMLEmptyChecker,
5588 wakaba 1.54 status => FEATURE_M12N10_REC_DEPRECATED |
5589     Whatpm::ContentChecker::FEATURE_DEPRECATED_SHOULD, ## [HTML4]
5590 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5591     prompt => sub {}, ## NOTE: Text [M12N]
5592     }, {
5593     %HTMLAttrStatus,
5594     class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5595     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5596     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5597     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5598     prompt => FEATURE_M12N10_REC_DEPRECATED,
5599 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
5600 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5601 wakaba 1.52 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5602     }),
5603     ## TODO: Tests
5604     ## TODO: Tests for <nest/> in <isindex>
5605 wakaba 1.66 check_start => sub {
5606     my ($self, $item, $element_state) = @_;
5607    
5608     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5609 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5610     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5611 wakaba 1.66 },
5612 wakaba 1.52 };
5613 wakaba 1.49
5614 wakaba 1.1 $Element->{$HTML_NS}->{script} = {
5615 wakaba 1.40 %HTMLChecker,
5616 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5617 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5618 wakaba 1.91 charset => sub {
5619     my ($self, $attr) = @_;
5620    
5621     unless ($attr->owner_element->has_attribute_ns (undef, 'src')) {
5622     $self->{onerror}->(type => 'attribute not allowed',
5623     node => $attr,
5624 wakaba 1.104 level => $self->{level}->{must});
5625 wakaba 1.91 }
5626    
5627     $HTMLCharsetChecker->($attr->value, @_);
5628     },
5629 wakaba 1.86 language => sub {}, ## NOTE: No syntax constraint according to HTML4.
5630 wakaba 1.91 src => $HTMLURIAttrChecker, ## TODO: pointed resource MUST be in type of type="" (resource error)
5631 wakaba 1.1 defer => $GetHTMLBooleanAttrChecker->('defer'),
5632     async => $GetHTMLBooleanAttrChecker->('async'),
5633 wakaba 1.91 type => $HTMLIMTAttrChecker, ## TODO: MUST NOT: |charset=""| parameter
5634 wakaba 1.49 }, {
5635     %HTMLAttrStatus,
5636     %HTMLM12NCommonAttrStatus,
5637 wakaba 1.50 async => FEATURE_HTML5_DEFAULT,
5638 wakaba 1.91 charset => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5639 wakaba 1.50 defer => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5640 wakaba 1.49 event => FEATURE_HTML4_REC_RESERVED,
5641     for => FEATURE_HTML4_REC_RESERVED,
5642 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5643 wakaba 1.49 language => FEATURE_M12N10_REC_DEPRECATED,
5644 wakaba 1.50 src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5645     type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5646 wakaba 1.9 }),
5647 wakaba 1.40 check_start => sub {
5648     my ($self, $item, $element_state) = @_;
5649 wakaba 1.1
5650 wakaba 1.40 if ($item->{node}->has_attribute_ns (undef, 'src')) {
5651     $element_state->{must_be_empty} = 1;
5652 wakaba 1.1 } else {
5653     ## NOTE: No content model conformance in HTML5 spec.
5654 wakaba 1.40 my $type = $item->{node}->get_attribute_ns (undef, 'type');
5655     my $language = $item->{node}->get_attribute_ns (undef, 'language');
5656 wakaba 1.1 if ((defined $type and $type eq '') or
5657     (defined $language and $language eq '')) {
5658     $type = 'text/javascript';
5659     } elsif (defined $type) {
5660     #
5661     } elsif (defined $language) {
5662     $type = 'text/' . $language;
5663     } else {
5664     $type = 'text/javascript';
5665     }
5666 wakaba 1.93
5667     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)]) {
5668     $type = "$1/$2";
5669     $type =~ tr/A-Z/a-z/; ## NOTE: ASCII case-insensitive
5670     ## TODO: Though we strip prameter here, it should not be ignored for the purpose of conformance checking...
5671     }
5672     $element_state->{script_type} = $type;
5673 wakaba 1.40 }
5674 wakaba 1.66
5675     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
5676 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5677     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5678 wakaba 1.107
5679     $element_state->{text} = '';
5680 wakaba 1.40 },
5681     check_child_element => sub {
5682     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5683     $child_is_transparent, $element_state) = @_;
5684     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5685     $self->{onerror}->(node => $child_el,
5686     type => 'element not allowed:minus',
5687 wakaba 1.104 level => $self->{level}->{must});
5688 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5689     #
5690     } else {
5691     if ($element_state->{must_be_empty}) {
5692     $self->{onerror}->(node => $child_el,
5693 wakaba 1.104 type => 'element not allowed:empty',
5694     level => $self->{level}->{must});
5695 wakaba 1.40 }
5696     }
5697     },
5698     check_child_text => sub {
5699     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5700     if ($has_significant and
5701     $element_state->{must_be_empty}) {
5702     $self->{onerror}->(node => $child_node,
5703 wakaba 1.104 type => 'character not allowed:empty',
5704     level => $self->{level}->{must});
5705 wakaba 1.40 }
5706 wakaba 1.115 $element_state->{text} .= $child_node->data;
5707 wakaba 1.40 },
5708     check_end => sub {
5709     my ($self, $item, $element_state) = @_;
5710     unless ($element_state->{must_be_empty}) {
5711 wakaba 1.93 if ($element_state->{script_type} =~ m![+/][Xx][Mm][Ll]\z!) {
5712     ## NOTE: XML content should be checked by THIS instance of checker
5713     ## as part of normal tree validation.
5714 wakaba 1.104 $self->{onerror}->(node => $item->{node},
5715     type => 'XML script lang',
5716     text => $element_state->{script_type},
5717     level => $self->{level}->{uncertain});
5718     ## ISSUE: Should we raise some kind of error for
5719     ## <script type="text/xml">aaaaa</script>?
5720     ## NOTE: ^^^ This is why we throw an "uncertain" error.
5721 wakaba 1.93 } else {
5722     $self->{onsubdoc}->({s => $element_state->{text},
5723     container_node => $item->{node},
5724     media_type => $element_state->{script_type},
5725     is_char_string => 1});
5726     }
5727 wakaba 1.40
5728     $HTMLChecker{check_end}->(@_);
5729 wakaba 1.1 }
5730     },
5731 wakaba 1.91 ## TODO: There MUST be |type| unless the script type is JavaScript. (resource error)
5732     ## NOTE: "When used to include script data, the script data must be embedded
5733     ## inline, the format of the data must be given using the type attribute,
5734     ## and the src attribute must not be specified." - not testable.
5735     ## TODO: It would be possible to err <script type=text/plain src=...>
5736 wakaba 1.1 };
5737 wakaba 1.25 ## ISSUE: Significant check and text child node
5738 wakaba 1.1
5739     ## NOTE: When script is disabled.
5740     $Element->{$HTML_NS}->{noscript} = {
5741 wakaba 1.40 %HTMLTransparentChecker,
5742 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5743     check_attrs => $GetHTMLAttrsChecker->({}, {
5744     %HTMLAttrStatus,
5745     %HTMLM12NCommonAttrStatus,
5746 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5747 wakaba 1.49 }),
5748 wakaba 1.40 check_start => sub {
5749     my ($self, $item, $element_state) = @_;
5750 wakaba 1.3
5751 wakaba 1.40 unless ($item->{node}->owner_document->manakai_is_html) {
5752 wakaba 1.104 $self->{onerror}->(node => $item->{node}, type => 'in XML:noscript',
5753     level => $self->{level}->{must});
5754 wakaba 1.3 }
5755    
5756 wakaba 1.40 unless ($self->{flag}->{in_head}) {
5757     $self->_add_minus_elements ($element_state,
5758     {$HTML_NS => {noscript => 1}});
5759     }
5760 wakaba 1.79
5761     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5762     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5763 wakaba 1.3 },
5764 wakaba 1.40 check_child_element => sub {
5765     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5766     $child_is_transparent, $element_state) = @_;
5767     if ($self->{flag}->{in_head}) {
5768     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5769     $self->{onerror}->(node => $child_el,
5770     type => 'element not allowed:minus',
5771 wakaba 1.104 level => $self->{level}->{must});
5772 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5773     #
5774     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'link') {
5775     #
5776     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') {
5777     if ($child_el->has_attribute_ns (undef, 'scoped')) {
5778     $self->{onerror}->(node => $child_el,
5779     type => 'element not allowed:head noscript',
5780 wakaba 1.104 level => $self->{level}->{must});
5781 wakaba 1.40 }
5782     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'meta') {
5783 wakaba 1.47 my $http_equiv_attr
5784     = $child_el->get_attribute_node_ns (undef, 'http-equiv');
5785     if ($http_equiv_attr) {
5786     ## TODO: case
5787     if (lc $http_equiv_attr->value eq 'content-type') {
5788 wakaba 1.40 $self->{onerror}->(node => $child_el,
5789 wakaba 1.34 type => 'element not allowed:head noscript',
5790 wakaba 1.104 level => $self->{level}->{must});
5791 wakaba 1.47 } else {
5792     #
5793 wakaba 1.3 }
5794 wakaba 1.47 } else {
5795     $self->{onerror}->(node => $child_el,
5796     type => 'element not allowed:head noscript',
5797 wakaba 1.104 level => $self->{level}->{must});
5798 wakaba 1.3 }
5799 wakaba 1.40 } else {
5800     $self->{onerror}->(node => $child_el,
5801     type => 'element not allowed:head noscript',
5802 wakaba 1.104 level => $self->{level}->{must});
5803 wakaba 1.40 }
5804     } else {
5805     $HTMLTransparentChecker{check_child_element}->(@_);
5806     }
5807     },
5808     check_child_text => sub {
5809     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5810     if ($self->{flag}->{in_head}) {
5811     if ($has_significant) {
5812     $self->{onerror}->(node => $child_node,
5813 wakaba 1.104 type => 'character not allowed',
5814     level => $self->{level}->{must});
5815 wakaba 1.3 }
5816     } else {
5817 wakaba 1.40 $HTMLTransparentChecker{check_child_text}->(@_);
5818     }
5819     },
5820     check_end => sub {
5821     my ($self, $item, $element_state) = @_;
5822     $self->_remove_minus_elements ($element_state);
5823     if ($self->{flag}->{in_head}) {
5824     $HTMLChecker{check_end}->(@_);
5825     } else {
5826     $HTMLPhrasingContentChecker{check_end}->(@_);
5827 wakaba 1.3 }
5828 wakaba 1.1 },
5829     };
5830 wakaba 1.3 ## ISSUE: Scripting is disabled: <head><noscript><html a></noscript></head>
5831 wakaba 1.1
5832     $Element->{$HTML_NS}->{'event-source'} = {
5833 wakaba 1.40 %HTMLEmptyChecker,
5834 wakaba 1.48 status => FEATURE_HTML5_LC,
5835 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5836 wakaba 1.1 src => $HTMLURIAttrChecker,
5837 wakaba 1.50 }, {
5838     %HTMLAttrStatus,
5839     src => FEATURE_HTML5_LC,
5840 wakaba 1.1 }),
5841 wakaba 1.66 check_start => sub {
5842     my ($self, $item, $element_state) = @_;
5843    
5844     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
5845 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5846     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5847 wakaba 1.66 },
5848 wakaba 1.1 };
5849    
5850     $Element->{$HTML_NS}->{details} = {
5851 wakaba 1.72 %HTMLFlowContentChecker,
5852 wakaba 1.48 status => FEATURE_HTML5_WD,
5853 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5854 wakaba 1.1 open => $GetHTMLBooleanAttrChecker->('open'),
5855 wakaba 1.50 }, {
5856     %HTMLAttrStatus,
5857 wakaba 1.59 open => FEATURE_HTML5_WD,
5858 wakaba 1.1 }),
5859 wakaba 1.72 ## NOTE: legend, Flow
5860 wakaba 1.43 check_child_element => sub {
5861     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5862     $child_is_transparent, $element_state) = @_;
5863     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5864     $self->{onerror}->(node => $child_el,
5865     type => 'element not allowed:minus',
5866 wakaba 1.104 level => $self->{level}->{must});
5867 wakaba 1.43 $element_state->{has_non_legend} = 1;
5868     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5869     #
5870     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') {
5871     if ($element_state->{has_non_legend}) {
5872     $self->{onerror}->(node => $child_el,
5873     type => 'element not allowed:details legend',
5874 wakaba 1.104 level => $self->{level}->{must});
5875 wakaba 1.43 }
5876     $element_state->{has_legend} = 1;
5877     $element_state->{has_non_legend} = 1;
5878     } else {
5879 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
5880 wakaba 1.43 $element_state->{has_non_legend} = 1 unless $child_is_transparent;
5881     ## ISSUE: |<details><object><legend>xx</legend></object>..</details>|
5882     ## is conforming?
5883     }
5884     },
5885     check_child_text => sub {
5886     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5887     if ($has_significant) {
5888     $element_state->{has_non_legend} = 1;
5889     }
5890     },
5891     check_end => sub {
5892     my ($self, $item, $element_state) = @_;
5893 wakaba 1.1
5894 wakaba 1.43 unless ($element_state->{has_legend}) {
5895     $self->{onerror}->(node => $item->{node},
5896 wakaba 1.110 type => 'child element missing',
5897 wakaba 1.104 text => 'legend',
5898     level => $self->{level}->{must});
5899 wakaba 1.43 }
5900    
5901 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
5902 wakaba 1.43 ## ISSUE: |<details><legend>aa</legend></details>| error?
5903 wakaba 1.1 },
5904     };
5905    
5906     $Element->{$HTML_NS}->{datagrid} = {
5907 wakaba 1.72 %HTMLFlowContentChecker,
5908 wakaba 1.48 status => FEATURE_HTML5_WD,
5909 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5910 wakaba 1.1 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5911     multiple => $GetHTMLBooleanAttrChecker->('multiple'),
5912 wakaba 1.50 }, {
5913     %HTMLAttrStatus,
5914     disabled => FEATURE_HTML5_WD,
5915     multiple => FEATURE_HTML5_WD,
5916 wakaba 1.1 }),
5917 wakaba 1.40 check_start => sub {
5918     my ($self, $item, $element_state) = @_;
5919 wakaba 1.1
5920 wakaba 1.40 $self->_add_minus_elements ($element_state,
5921     {$HTML_NS => {a => 1, datagrid => 1}});
5922     $element_state->{phase} = 'any';
5923 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5924     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5925 wakaba 1.40 },
5926 wakaba 1.95 ## NOTE: Flow -(text* (table|select|datalist) Flow*) | table | select |
5927     ## datalist | Empty
5928 wakaba 1.40 check_child_element => sub {
5929     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5930     $child_is_transparent, $element_state) = @_;
5931     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5932     $self->{onerror}->(node => $child_el,
5933     type => 'element not allowed:minus',
5934 wakaba 1.104 level => $self->{level}->{must});
5935 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5936     #
5937 wakaba 1.72 } elsif ($element_state->{phase} eq 'flow') {
5938     if ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) {
5939 wakaba 1.44 if (not $element_state->{has_element} and
5940 wakaba 1.40 $child_nsuri eq $HTML_NS and
5941 wakaba 1.95 {
5942     table => 1, select => 1, datalist => 1,
5943     }->{$child_ln}) {
5944 wakaba 1.40 $self->{onerror}->(node => $child_el,
5945 wakaba 1.104 type => 'element not allowed',
5946     level => $self->{level}->{must});
5947 wakaba 1.40 } else {
5948 wakaba 1.8 #
5949 wakaba 1.1 }
5950 wakaba 1.40 } else {
5951     $self->{onerror}->(node => $child_el,
5952 wakaba 1.104 type => 'element not allowed',
5953     level => $self->{level}->{must});
5954 wakaba 1.40 }
5955 wakaba 1.43 $element_state->{has_element} = 1;
5956 wakaba 1.40 } elsif ($element_state->{phase} eq 'any') {
5957     if ($child_nsuri eq $HTML_NS and
5958     {table => 1, select => 1, datalist => 1}->{$child_ln}) {
5959     $element_state->{phase} = 'none';
5960 wakaba 1.72 } elsif ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) {
5961 wakaba 1.40 $element_state->{has_element} = 1;
5962 wakaba 1.72 $element_state->{phase} = 'flow';
5963 wakaba 1.40 } else {
5964     $self->{onerror}->(node => $child_el,
5965 wakaba 1.104 type => 'element not allowed',
5966     level => $self->{level}->{must});
5967 wakaba 1.40 }
5968     } elsif ($element_state->{phase} eq 'none') {
5969     $self->{onerror}->(node => $child_el,
5970 wakaba 1.104 type => 'element not allowed',
5971     level => $self->{level}->{must});
5972 wakaba 1.40 } else {
5973     die "check_child_element: Bad |datagrid| phase: $element_state->{phase}";
5974     }
5975     },
5976     check_child_text => sub {
5977     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5978     if ($has_significant) {
5979 wakaba 1.72 if ($element_state->{phase} eq 'flow') {
5980 wakaba 1.40 #
5981     } elsif ($element_state->{phase} eq 'any') {
5982 wakaba 1.72 $element_state->{phase} = 'flow';
5983 wakaba 1.40 } else {
5984     $self->{onerror}->(node => $child_node,
5985 wakaba 1.104 type => 'character not allowed',
5986     level => $self->{level}->{must});
5987 wakaba 1.1 }
5988     }
5989 wakaba 1.40 },
5990     check_end => sub {
5991     my ($self, $item, $element_state) = @_;
5992     $self->_remove_minus_elements ($element_state);
5993 wakaba 1.1
5994 wakaba 1.95 if ($element_state->{phase} eq 'flow') {
5995     if ($element_state->{has_significant}) {
5996     $item->{real_parent_state}->{has_significant} = 1;
5997     } elsif ($item->{transparent}) {
5998     #
5999     } else {
6000     $self->{onerror}->(node => $item->{node},
6001 wakaba 1.104 type => 'no significant content',
6002 wakaba 1.110 level => $self->{level}->{should});
6003 wakaba 1.95 }
6004     } else {
6005     ## NOTE: Since the content model explicitly allows a |datagird| element
6006     ## being empty, we don't raise "no significant content" error for this
6007     ## element when there is no element. (We should raise an error for
6008     ## |<datagrid><br></datagrid>|, however.)
6009     ## NOTE: As a side-effect, when the |datagrid| element only contains
6010     ## non-conforming content, then the |phase| flag has not changed from
6011     ## |any|, no "no significant content" error is raised neither.
6012     ## NOTE: Another side-effect of the current implementation:
6013     ## |<daragrid><datagrid/></datagrid>| has no "no significant content"
6014     ## error at all.
6015 wakaba 1.40 $HTMLChecker{check_end}->(@_);
6016     }
6017     },
6018 wakaba 1.1 };
6019    
6020     $Element->{$HTML_NS}->{command} = {
6021 wakaba 1.40 %HTMLEmptyChecker,
6022 wakaba 1.48 status => FEATURE_HTML5_WD,
6023 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6024 wakaba 1.1 checked => $GetHTMLBooleanAttrChecker->('checked'),
6025     default => $GetHTMLBooleanAttrChecker->('default'),
6026     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
6027     icon => $HTMLURIAttrChecker,
6028     label => sub { }, ## NOTE: No conformance creteria
6029     radiogroup => sub { }, ## NOTE: No conformance creteria
6030     type => sub {
6031     my ($self, $attr) = @_;
6032     my $value = $attr->value;
6033     unless ({command => 1, checkbox => 1, radio => 1}->{$value}) {
6034 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'invalid attribute value',
6035     level => $self->{level}->{must});
6036 wakaba 1.1 }
6037     },
6038 wakaba 1.50 }, {
6039     %HTMLAttrStatus,
6040     checked => FEATURE_HTML5_WD,
6041     default => FEATURE_HTML5_WD,
6042     disabled => FEATURE_HTML5_WD,
6043     icon => FEATURE_HTML5_WD,
6044     label => FEATURE_HTML5_WD,
6045     radiogroup => FEATURE_HTML5_WD,
6046     type => FEATURE_HTML5_WD,
6047 wakaba 1.1 }),
6048 wakaba 1.66 check_start => sub {
6049     my ($self, $item, $element_state) = @_;
6050    
6051     $element_state->{uri_info}->{icon}->{type}->{embedded} = 1;
6052 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6053     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6054 wakaba 1.66 },
6055 wakaba 1.115 };
6056    
6057     $Element->{$HTML_NS}->{bb} = {
6058     %HTMLPhrasingContentChecker,
6059     status => FEATURE_HTML5_DEFAULT,
6060     check_attrs => $GetHTMLAttrsChecker->({
6061     type => $GetHTMLEnumeratedAttrChecker->({makeapp => 1}),
6062     }, {
6063     %HTMLAttrStatus,
6064     type => FEATURE_HTML5_DEFAULT,
6065     }),
6066     ## TODO: no interactive content descendant
6067 wakaba 1.1 };
6068    
6069     $Element->{$HTML_NS}->{menu} = {
6070 wakaba 1.40 %HTMLPhrasingContentChecker,
6071 wakaba 1.54 #status => FEATURE_M12N10_REC_DEPRECATED | FEATURE_HTML5_WD,
6072     status => FEATURE_M12N10_REC | FEATURE_HTML5_WD,
6073     ## NOTE: We don't want any |menu| element warned as deprecated.
6074 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6075 wakaba 1.1 autosubmit => $GetHTMLBooleanAttrChecker->('autosubmit'),
6076 wakaba 1.68 compact => $GetHTMLBooleanAttrChecker->('compact'),
6077 wakaba 1.1 id => sub {
6078     ## NOTE: same as global |id=""|, with |$self->{menu}| registeration
6079     my ($self, $attr) = @_;
6080     my $value = $attr->value;
6081     if (length $value > 0) {
6082     if ($self->{id}->{$value}) {
6083 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'duplicate ID',
6084     level => $self->{level}->{must});
6085 wakaba 1.1 push @{$self->{id}->{$value}}, $attr;
6086     } else {
6087     $self->{id}->{$value} = [$attr];
6088     }
6089     } else {
6090     ## NOTE: MUST contain at least one character
6091 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'empty attribute value',
6092     level => $self->{level}->{must});
6093 wakaba 1.1 }
6094     if ($value =~ /[\x09-\x0D\x20]/) {
6095 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'space in ID',
6096     level => $self->{level}->{must});
6097 wakaba 1.1 }
6098     $self->{menu}->{$value} ||= $attr;
6099     ## ISSUE: <menu id=""><p contextmenu=""> match?
6100     },
6101     label => sub { }, ## NOTE: No conformance creteria
6102     type => $GetHTMLEnumeratedAttrChecker->({context => 1, toolbar => 1}),
6103 wakaba 1.49 }, {
6104     %HTMLAttrStatus,
6105     %HTMLM12NCommonAttrStatus,
6106 wakaba 1.61 align => FEATURE_HTML2X_RFC,
6107 wakaba 1.113 autosubmit => FEATURE_HTML5_DROPPED,
6108 wakaba 1.49 compat => FEATURE_M12N10_REC_DEPRECATED,
6109 wakaba 1.50 label => FEATURE_HTML5_WD,
6110     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6111 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
6112     sdapref => FEATURE_HTML20_RFC,
6113 wakaba 1.50 type => FEATURE_HTML5_WD,
6114 wakaba 1.1 }),
6115 wakaba 1.40 check_start => sub {
6116     my ($self, $item, $element_state) = @_;
6117     $element_state->{phase} = 'li or phrasing';
6118     $element_state->{in_menu_original} = $self->{flag}->{in_menu};
6119     $self->{flag}->{in_menu} = 1;
6120 wakaba 1.79
6121     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6122     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6123 wakaba 1.40 },
6124     check_child_element => sub {
6125     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6126     $child_is_transparent, $element_state) = @_;
6127     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
6128     $self->{onerror}->(node => $child_el,
6129     type => 'element not allowed:minus',
6130 wakaba 1.104 level => $self->{level}->{must});
6131 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6132     #
6133     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'li') {
6134     if ($element_state->{phase} eq 'li') {
6135     #
6136     } elsif ($element_state->{phase} eq 'li or phrasing') {
6137     $element_state->{phase} = 'li';
6138     } else {
6139 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
6140     level => $self->{level}->{must});
6141 wakaba 1.40 }
6142     } elsif ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
6143     if ($element_state->{phase} eq 'phrasing') {
6144     #
6145     } elsif ($element_state->{phase} eq 'li or phrasing') {
6146     $element_state->{phase} = 'phrasing';
6147     } else {
6148 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
6149     level => $self->{level}->{must});
6150 wakaba 1.40 }
6151     } else {
6152 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
6153     level => $self->{level}->{must});
6154 wakaba 1.40 }
6155     },
6156     check_child_text => sub {
6157     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6158     if ($has_significant) {
6159     if ($element_state->{phase} eq 'phrasing') {
6160     #
6161     } elsif ($element_state->{phase} eq 'li or phrasing') {
6162     $element_state->{phase} = 'phrasing';
6163     } else {
6164     $self->{onerror}->(node => $child_node,
6165 wakaba 1.104 type => 'character not allowed',
6166     level => $self->{level}->{must});
6167 wakaba 1.1 }
6168     }
6169 wakaba 1.40 },
6170     check_end => sub {
6171     my ($self, $item, $element_state) = @_;
6172     delete $self->{flag}->{in_menu} unless $element_state->{in_menu_original};
6173    
6174     if ($element_state->{phase} eq 'li') {
6175     $HTMLChecker{check_end}->(@_);
6176     } else { # 'phrasing' or 'li or phrasing'
6177     $HTMLPhrasingContentChecker{check_end}->(@_);
6178 wakaba 1.1 }
6179     },
6180 wakaba 1.8 };
6181    
6182     $Element->{$HTML_NS}->{datatemplate} = {
6183 wakaba 1.40 %HTMLChecker,
6184 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
6185 wakaba 1.40 check_child_element => sub {
6186     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6187     $child_is_transparent, $element_state) = @_;
6188     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
6189     $self->{onerror}->(node => $child_el,
6190     type => 'element not allowed:minus',
6191 wakaba 1.104 level => $self->{level}->{must});
6192 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6193     #
6194     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'rule') {
6195     #
6196     } else {
6197     $self->{onerror}->(node => $child_el,
6198 wakaba 1.104 type => 'element not allowed:datatemplate',
6199     level => $self->{level}->{must});
6200 wakaba 1.40 }
6201     },
6202     check_child_text => sub {
6203     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6204     if ($has_significant) {
6205 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
6206     level => $self->{level}->{must});
6207 wakaba 1.8 }
6208     },
6209     is_xml_root => 1,
6210     };
6211    
6212     $Element->{$HTML_NS}->{rule} = {
6213 wakaba 1.40 %HTMLChecker,
6214 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
6215 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6216 wakaba 1.23 condition => $HTMLSelectorsAttrChecker,
6217 wakaba 1.92 mode => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(),
6218 wakaba 1.50 }, {
6219     %HTMLAttrStatus,
6220     condition => FEATURE_HTML5_AT_RISK,
6221     mode => FEATURE_HTML5_AT_RISK,
6222 wakaba 1.8 }),
6223 wakaba 1.40 check_start => sub {
6224     my ($self, $item, $element_state) = @_;
6225 wakaba 1.79
6226 wakaba 1.40 $self->_add_plus_elements ($element_state, {$HTML_NS => {nest => 1}});
6227 wakaba 1.79 $element_state->{in_rule_original} = $self->{flag}->{in_rule};
6228     $self->{flag}->{in_rule} = 1;
6229    
6230     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6231     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6232 wakaba 1.40 },
6233     check_child_element => sub { },
6234     check_child_text => sub { },
6235     check_end => sub {
6236     my ($self, $item, $element_state) = @_;
6237 wakaba 1.79
6238 wakaba 1.40 $self->_remove_plus_elements ($element_state);
6239 wakaba 1.79 delete $self->{flag}->{in_rule} unless $element_state->{in_rule_original};
6240    
6241 wakaba 1.40 $HTMLChecker{check_end}->(@_);
6242 wakaba 1.8 },
6243     ## NOTE: "MAY be anything that, when the parent |datatemplate|
6244     ## is applied to some conforming data, results in a conforming DOM tree.":
6245     ## We don't check against this.
6246     };
6247    
6248     $Element->{$HTML_NS}->{nest} = {
6249 wakaba 1.40 %HTMLEmptyChecker,
6250 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
6251 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6252 wakaba 1.23 filter => $HTMLSelectorsAttrChecker,
6253     mode => sub {
6254     my ($self, $attr) = @_;
6255     my $value = $attr->value;
6256     if ($value !~ /\A[^\x09-\x0D\x20]+\z/) {
6257 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'mode:syntax error',
6258     level => $self->{level}->{must});
6259 wakaba 1.23 }
6260     },
6261 wakaba 1.50 }, {
6262     %HTMLAttrStatus,
6263     filter => FEATURE_HTML5_AT_RISK,
6264     mode => FEATURE_HTML5_AT_RISK,
6265 wakaba 1.8 }),
6266 wakaba 1.1 };
6267    
6268     $Element->{$HTML_NS}->{legend} = {
6269 wakaba 1.40 %HTMLPhrasingContentChecker,
6270 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6271 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6272 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
6273 wakaba 1.52 # align => $GetHTMLEnumeratedAttrChecker->({
6274     # top => 1, bottom => 1, left => 1, right => 1,
6275     # }),
6276     }, {
6277 wakaba 1.49 %HTMLAttrStatus,
6278     %HTMLM12NCommonAttrStatus,
6279     accesskey => FEATURE_M12N10_REC,
6280     align => FEATURE_M12N10_REC_DEPRECATED,
6281 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6282 wakaba 1.49 }),
6283 wakaba 1.1 };
6284    
6285     $Element->{$HTML_NS}->{div} = {
6286 wakaba 1.72 %HTMLFlowContentChecker,
6287 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
6288 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
6289     align => $GetHTMLEnumeratedAttrChecker->({
6290     left => 1, center => 1, right => 1, justify => 1,
6291     }),
6292     }, {
6293 wakaba 1.49 %HTMLAttrStatus,
6294 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
6295 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
6296     datafld => FEATURE_HTML4_REC_RESERVED,
6297     dataformatas => FEATURE_HTML4_REC_RESERVED,
6298     datasrc => FEATURE_HTML4_REC_RESERVED,
6299 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6300 wakaba 1.49 }),
6301 wakaba 1.66 check_start => sub {
6302     my ($self, $item, $element_state) = @_;
6303    
6304     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
6305 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6306     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6307 wakaba 1.66 },
6308 wakaba 1.1 };
6309    
6310 wakaba 1.64 $Element->{$HTML_NS}->{center} = {
6311 wakaba 1.72 %HTMLFlowContentChecker,
6312 wakaba 1.64 status => FEATURE_M12N10_REC_DEPRECATED,
6313     check_attrs => $GetHTMLAttrsChecker->({}, {
6314     %HTMLAttrStatus,
6315     %HTMLM12NCommonAttrStatus,
6316     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6317     }),
6318     };
6319    
6320 wakaba 1.1 $Element->{$HTML_NS}->{font} = {
6321 wakaba 1.40 %HTMLTransparentChecker,
6322 wakaba 1.78 status => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC_DEPRECATED,
6323 wakaba 1.70 check_attrs => $GetHTMLAttrsChecker->({
6324     ## TODO: HTML4 |size|, |color|, |face|
6325 wakaba 1.49 }, {
6326     %HTMLAttrStatus,
6327 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6328 wakaba 1.49 color => FEATURE_M12N10_REC_DEPRECATED,
6329 wakaba 1.50 dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6330 wakaba 1.49 face => FEATURE_M12N10_REC_DEPRECATED,
6331 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6332     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6333 wakaba 1.49 size => FEATURE_M12N10_REC_DEPRECATED,
6334 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6335 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6336 wakaba 1.49 }),
6337 wakaba 1.78 ## NOTE: When the |font| element was defined in the HTML5 specification,
6338     ## it is allowed only in a document with the WYSIWYG signature. The
6339     ## checker does not check whether there is the signature, since the
6340     ## signature is dropped, too, and has never been implemented. (In addition,
6341     ## for any |font| element an "element not defined" error is raised anyway,
6342     ## such that we don't have to raise an additional error.)
6343 wakaba 1.1 };
6344 wakaba 1.49
6345 wakaba 1.64 $Element->{$HTML_NS}->{basefont} = {
6346     %HTMLEmptyChecker,
6347     status => FEATURE_M12N10_REC_DEPRECATED,
6348     check_attrs => $GetHTMLAttrsChecker->({
6349     ## TODO: color, face, size
6350     }, {
6351     %HTMLAttrStatus,
6352     color => FEATURE_M12N10_REC_DEPRECATED,
6353     face => FEATURE_M12N10_REC_DEPRECATED,
6354     #id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
6355     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6356     size => FEATURE_M12N10_REC_DEPRECATED,
6357     }),
6358     };
6359    
6360 wakaba 1.49 ## TODO: frameset FEATURE_M12N10_REC
6361     ## class title id cols rows onload onunload style(x10)
6362     ## frame frameborder longdesc marginheight marginwidth noresize scrolling src name(deprecated) class,id,title,style(x10)
6363     ## noframes Common, lang(xhtml10)
6364    
6365 wakaba 1.100 ## TODO: CR: rbc rtc @rbspan (M12NXHTML2Common)
6366 wakaba 1.56
6367 wakaba 1.61 ## TODO: xmp, listing, plaintext FEATURE_HTML32_REC_OBSOLETE
6368     ## TODO: ^^^ lang, dir, id, class [HTML 2.x] sdaform [HTML 2.0]
6369     ## xmp, listing sdapref[HTML2,0]
6370    
6371 wakaba 1.56 =pod
6372    
6373     WF2: Documents MUST comply to [CHARMOD].
6374     WF2: Vencor extensions MUST NOT be used.
6375    
6376 wakaba 1.61 HTML 2.0 nextid @n
6377    
6378     RFC 2659: CERTS CRYPTOPTS
6379    
6380     ISO-HTML: pre-html, divN
6381 wakaba 1.82
6382     XHTML2: blockcode (Common), h (Common), separator (Common), l (Common),
6383     di (Common), nl (Common), handler (Common, type), standby (Common),
6384     summary (Common)
6385    
6386 wakaba 1.97 Access & XHTML2: access (LC)
6387 wakaba 1.82
6388     XML Events & XForms (for XHTML2 support; very, very low priority)
6389 wakaba 1.61
6390 wakaba 1.56 =cut
6391 wakaba 1.61
6392     ## NOTE: Where RFC 2659 allows additional attributes is unclear.
6393     ## We added them only to |a|. |link| and |form| might also allow them
6394     ## in theory.
6395 wakaba 1.1
6396     $Whatpm::ContentChecker::Namespace->{$HTML_NS}->{loaded} = 1;
6397    
6398     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24